sync

Backup service to store encrypted wallet databases (experimental)
Log | Files | Refs | Submodules | README | LICENSE

plugin_syncdb_postgres.c (42389B)


      1 /*
      2   This file is part of TALER
      3   (C) 2014--2022 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Lesser 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 ANASTASISABILITY 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  * @file syncdb/plugin_syncdb_postgres.c
     18  * @brief database helper functions for postgres used by sync
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include <gnunet/gnunet_util_lib.h>
     23 #include <gnunet/gnunet_db_lib.h>
     24 #include <gnunet/gnunet_pq_lib.h>
     25 #include <taler/taler_pq_lib.h>
     26 #include "sync_database_plugin.h"
     27 #include "sync_database_lib.h"
     28 
     29 /**
     30  * Type of the "cls" argument given to each of the functions in
     31  * our API.
     32  */
     33 struct PostgresClosure
     34 {
     35 
     36   /**
     37    * Postgres connection handle.
     38    */
     39   struct GNUNET_PQ_Context *conn;
     40 
     41   /**
     42    * Directory with SQL statements to run to create tables.
     43    */
     44   char *sql_dir;
     45 
     46   /**
     47    * Underlying configuration.
     48    */
     49   const struct GNUNET_CONFIGURATION_Handle *cfg;
     50 
     51   /**
     52    * Name of the currently active transaction, NULL if none is active.
     53    */
     54   const char *transaction_name;
     55 
     56   /**
     57    * Currency we accept payments in.
     58    */
     59   char *currency;
     60 
     61   /**
     62    * Did we initialize the prepared statements
     63    * for this session?
     64    */
     65   bool init;
     66 
     67 };
     68 
     69 
     70 /**
     71  * Drop sync tables
     72  *
     73  * @param cls closure our `struct Plugin`
     74  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
     75  */
     76 static enum GNUNET_GenericReturnValue
     77 postgres_drop_tables (void *cls)
     78 {
     79   struct PostgresClosure *pg = cls;
     80   struct GNUNET_PQ_Context *conn;
     81   enum GNUNET_GenericReturnValue ret;
     82 
     83   if (NULL != pg->conn)
     84   {
     85     GNUNET_PQ_disconnect (pg->conn);
     86     pg->conn = NULL;
     87     pg->init = false;
     88   }
     89   conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
     90                                      "syncdb-postgres",
     91                                      NULL,
     92                                      NULL,
     93                                      NULL);
     94   if (NULL == conn)
     95     return GNUNET_SYSERR;
     96   ret = GNUNET_PQ_exec_sql (conn,
     97                             "drop");
     98   GNUNET_PQ_disconnect (conn);
     99   return ret;
    100 }
    101 
    102 
    103 /**
    104  * Establish connection to the database.
    105  *
    106  * @param cls plugin context
    107  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
    108  */
    109 static enum GNUNET_GenericReturnValue
    110 prepare_statements (void *cls)
    111 {
    112   struct PostgresClosure *pg = cls;
    113   struct GNUNET_PQ_PreparedStatement ps[] = {
    114     GNUNET_PQ_make_prepare ("account_insert",
    115                             "INSERT INTO accounts "
    116                             "(account_pub"
    117                             ",expiration_date"
    118                             ") VALUES "
    119                             "($1,$2);"),
    120     GNUNET_PQ_make_prepare ("payment_insert",
    121                             "INSERT INTO payments "
    122                             "(account_pub"
    123                             ",order_id"
    124                             ",token"
    125                             ",timestamp"
    126                             ",amount"
    127                             ") VALUES "
    128                             "($1,$2,$3,$4,$5);"),
    129     GNUNET_PQ_make_prepare ("payment_done",
    130                             "UPDATE payments "
    131                             "SET"
    132                             " paid=TRUE "
    133                             "WHERE"
    134                             "  order_id=$1"
    135                             " AND"
    136                             "  account_pub=$2"
    137                             " AND"
    138                             "  paid=FALSE;"),
    139     GNUNET_PQ_make_prepare ("account_update",
    140                             "UPDATE accounts "
    141                             "SET"
    142                             " expiration_date=$1 "
    143                             "WHERE"
    144                             " account_pub=$2;"),
    145     GNUNET_PQ_make_prepare ("account_select",
    146                             "SELECT"
    147                             " expiration_date "
    148                             "FROM"
    149                             " accounts "
    150                             "WHERE"
    151                             " account_pub=$1;"),
    152     GNUNET_PQ_make_prepare ("payments_select",
    153                             "SELECT"
    154                             " account_pub"
    155                             ",order_id"
    156                             ",amount"
    157                             " FROM payments"
    158                             " WHERE paid=FALSE;"),
    159     GNUNET_PQ_make_prepare ("payments_select_by_account",
    160                             "SELECT"
    161                             " timestamp"
    162                             ",order_id"
    163                             ",token"
    164                             ",amount"
    165                             " FROM payments"
    166                             " WHERE"
    167                             "  paid=FALSE"
    168                             " AND"
    169                             "  account_pub=$1;"),
    170     GNUNET_PQ_make_prepare ("gc_accounts",
    171                             "DELETE FROM accounts "
    172                             "WHERE"
    173                             " expiration_date < $1;"),
    174     GNUNET_PQ_make_prepare ("gc_pending_payments",
    175                             "DELETE FROM payments "
    176                             "WHERE"
    177                             "  paid=FALSE"
    178                             " AND"
    179                             "  timestamp < $1;"),
    180     GNUNET_PQ_make_prepare ("backup_insert",
    181                             "INSERT INTO backups "
    182                             "(account_pub"
    183                             ",account_sig"
    184                             ",prev_hash"
    185                             ",backup_hash"
    186                             ",data"
    187                             ") VALUES "
    188                             "($1,$2,$3,$4,$5);"),
    189     GNUNET_PQ_make_prepare ("backup_update",
    190                             "UPDATE backups "
    191                             " SET"
    192                             " backup_hash=$1"
    193                             ",account_sig=$2"
    194                             ",prev_hash=$3"
    195                             ",data=$4"
    196                             " WHERE"
    197                             "   account_pub=$5"
    198                             "  AND"
    199                             "   backup_hash=$6;"),
    200     GNUNET_PQ_make_prepare ("backup_select_hash",
    201                             "SELECT "
    202                             " backup_hash "
    203                             "FROM"
    204                             " backups "
    205                             "WHERE"
    206                             " account_pub=$1;"),
    207     GNUNET_PQ_make_prepare ("backup_select",
    208                             "SELECT "
    209                             " account_sig"
    210                             ",prev_hash"
    211                             ",backup_hash"
    212                             ",data "
    213                             "FROM"
    214                             " backups "
    215                             "WHERE"
    216                             " account_pub=$1;"),
    217     GNUNET_PQ_make_prepare ("do_commit",
    218                             "COMMIT"),
    219     GNUNET_PQ_PREPARED_STATEMENT_END
    220   };
    221   enum GNUNET_GenericReturnValue ret;
    222 
    223   ret = GNUNET_PQ_prepare_statements (pg->conn,
    224                                       ps);
    225   if (GNUNET_OK != ret)
    226     return ret;
    227   pg->init = true;
    228   return GNUNET_OK;
    229 }
    230 
    231 
    232 /**
    233  * Connect to the database if the connection does not exist yet.
    234  *
    235  * @param pg the plugin-specific state
    236  * @param skip_prepare true if we should skip prepared statement setup
    237  * @return #GNUNET_OK on success
    238  */
    239 static enum GNUNET_GenericReturnValue
    240 internal_setup (struct PostgresClosure *pg)
    241 {
    242   if (NULL == pg->conn)
    243   {
    244 #if AUTO_EXPLAIN
    245     /* Enable verbose logging to see where queries do not
    246        properly use indices */
    247     struct GNUNET_PQ_ExecuteStatement es[] = {
    248       GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"),
    249       GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"),
    250       GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"),
    251       GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"),
    252       /* https://wiki.postgresql.org/wiki/Serializable suggests to really
    253          force the default to 'serializable' if SSI is to be used. */
    254       GNUNET_PQ_make_try_execute (
    255         "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
    256       GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
    257       GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
    258       GNUNET_PQ_make_execute ("SET search_path TO sync;"),
    259       GNUNET_PQ_EXECUTE_STATEMENT_END
    260     };
    261 #else
    262     struct GNUNET_PQ_ExecuteStatement es[] = {
    263       GNUNET_PQ_make_execute ("SET search_path TO sync;"),
    264       GNUNET_PQ_EXECUTE_STATEMENT_END
    265     };
    266 #endif
    267     struct GNUNET_PQ_Context *db_conn;
    268 
    269     db_conn = GNUNET_PQ_connect_with_cfg2 (pg->cfg,
    270                                            "syncdb-postgres",
    271                                            "sync-",
    272                                            es,
    273                                            NULL,
    274                                            GNUNET_PQ_FLAG_CHECK_CURRENT);
    275     if (NULL == db_conn)
    276       return GNUNET_SYSERR;
    277     pg->conn = db_conn;
    278   }
    279   if (NULL == pg->transaction_name)
    280     GNUNET_PQ_reconnect_if_down (pg->conn);
    281   if (pg->init)
    282     return GNUNET_OK;
    283   return prepare_statements (pg);
    284 }
    285 
    286 
    287 /**
    288  * Do a pre-flight check that we are not in an uncommitted transaction.
    289  * If we are, try to commit the previous transaction and output a warning.
    290  * Does not return anything, as we will continue regardless of the outcome.
    291  *
    292  * @param cls the `struct PostgresClosure` with the plugin-specific state
    293  * @return #GNUNET_OK if everything is fine
    294  *         #GNUNET_NO if a transaction was rolled back
    295  *         #GNUNET_SYSERR on hard errors
    296  */
    297 static enum GNUNET_GenericReturnValue
    298 postgres_preflight (void *cls)
    299 {
    300   struct PostgresClosure *pg = cls;
    301   struct GNUNET_PQ_ExecuteStatement es[] = {
    302     GNUNET_PQ_make_execute ("ROLLBACK"),
    303     GNUNET_PQ_EXECUTE_STATEMENT_END
    304   };
    305 
    306   if (! pg->init)
    307   {
    308     if (GNUNET_OK !=
    309         internal_setup (pg))
    310     {
    311       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    312                   "Failed to ensure DB is initialized\n");
    313       return GNUNET_SYSERR;
    314     }
    315   }
    316   if (NULL == pg->transaction_name)
    317     return GNUNET_OK; /* all good */
    318   if (GNUNET_OK ==
    319       GNUNET_PQ_exec_statements (pg->conn,
    320                                  es))
    321   {
    322     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    323                 "BUG: Preflight check rolled back transaction `%s'!\n",
    324                 pg->transaction_name);
    325   }
    326   else
    327   {
    328     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    329                 "BUG: Preflight check failed to rollback transaction `%s'!\n",
    330                 pg->transaction_name);
    331   }
    332   pg->transaction_name = NULL;
    333   return GNUNET_NO;
    334 }
    335 
    336 
    337 /**
    338  * Check that the database connection is still up.
    339  *
    340  * @param cls a `struct PostgresClosure` with connection to check
    341  */
    342 static void
    343 check_connection (void *cls)
    344 {
    345   struct PostgresClosure *pg = cls;
    346 
    347   GNUNET_PQ_reconnect_if_down (pg->conn);
    348 }
    349 
    350 
    351 /**
    352  * Start a transaction.
    353  *
    354  * @param cls the `struct PostgresClosure` with the plugin-specific state
    355  * @param name unique name identifying the transaction (for debugging),
    356  *             must point to a constant
    357  * @return #GNUNET_OK on success
    358  */
    359 static enum GNUNET_GenericReturnValue
    360 begin_transaction (void *cls,
    361                    const char *name)
    362 {
    363   struct PostgresClosure *pg = cls;
    364   struct GNUNET_PQ_ExecuteStatement es[] = {
    365     GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"),
    366     GNUNET_PQ_EXECUTE_STATEMENT_END
    367   };
    368 
    369   check_connection (pg);
    370   postgres_preflight (pg);
    371   pg->transaction_name = name;
    372   if (GNUNET_OK !=
    373       GNUNET_PQ_exec_statements (pg->conn,
    374                                  es))
    375   {
    376     TALER_LOG_ERROR ("Failed to start transaction\n");
    377     GNUNET_break (0);
    378     return GNUNET_SYSERR;
    379   }
    380   return GNUNET_OK;
    381 }
    382 
    383 
    384 /**
    385  * Roll back the current transaction of a database connection.
    386  *
    387  * @param cls the `struct PostgresClosure` with the plugin-specific state
    388  */
    389 static void
    390 rollback (void *cls)
    391 {
    392   struct PostgresClosure *pg = cls;
    393   struct GNUNET_PQ_ExecuteStatement es[] = {
    394     GNUNET_PQ_make_execute ("ROLLBACK"),
    395     GNUNET_PQ_EXECUTE_STATEMENT_END
    396   };
    397 
    398   if (GNUNET_OK !=
    399       GNUNET_PQ_exec_statements (pg->conn,
    400                                  es))
    401   {
    402     TALER_LOG_ERROR ("Failed to rollback transaction\n");
    403     GNUNET_break (0);
    404   }
    405   pg->transaction_name = NULL;
    406 }
    407 
    408 
    409 /**
    410  * Commit the current transaction of a database connection.
    411  *
    412  * @param cls the `struct PostgresClosure` with the plugin-specific state
    413  * @return transaction status code
    414  */
    415 static enum GNUNET_DB_QueryStatus
    416 commit_transaction (void *cls)
    417 {
    418   struct PostgresClosure *pg = cls;
    419   enum GNUNET_DB_QueryStatus qs;
    420   struct GNUNET_PQ_QueryParam no_params[] = {
    421     GNUNET_PQ_query_param_end
    422   };
    423 
    424   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    425                                            "do_commit",
    426                                            no_params);
    427   pg->transaction_name = NULL;
    428   return qs;
    429 }
    430 
    431 
    432 /**
    433  * Function called to perform "garbage collection" on the
    434  * database, expiring records we no longer require.  Deletes
    435  * all user records that are not paid up (and by cascade deletes
    436  * the associated recovery documents). Also deletes expired
    437  * truth and financial records older than @a fin_expire.
    438  *
    439  * @param cls closure
    440  * @param expire_backups backups older than the given time stamp should be garbage collected
    441  * @param expire_pending_payments payments still pending from since before
    442  *            this value should be garbage collected
    443  * @return transaction status
    444  */
    445 static enum GNUNET_DB_QueryStatus
    446 postgres_gc (void *cls,
    447              struct GNUNET_TIME_Absolute expire_backups,
    448              struct GNUNET_TIME_Absolute expire_pending_payments)
    449 {
    450   struct PostgresClosure *pg = cls;
    451   struct GNUNET_PQ_QueryParam params[] = {
    452     GNUNET_PQ_query_param_absolute_time (&expire_backups),
    453     GNUNET_PQ_query_param_end
    454   };
    455   struct GNUNET_PQ_QueryParam params2[] = {
    456     GNUNET_PQ_query_param_absolute_time (&expire_pending_payments),
    457     GNUNET_PQ_query_param_end
    458   };
    459   enum GNUNET_DB_QueryStatus qs;
    460 
    461   check_connection (pg);
    462   postgres_preflight (pg);
    463   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    464                                            "gc_accounts",
    465                                            params);
    466   if (qs < 0)
    467     return qs;
    468   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    469                                              "gc_pending_payments",
    470                                              params2);
    471 }
    472 
    473 
    474 /**
    475  * Store payment. Used to begin a payment, not indicative
    476  * that the payment actually was made. (That is done
    477  * when we increment the account's lifetime.)
    478  *
    479  * @param cls closure
    480  * @param account_pub account to store @a backup under
    481  * @param order_id order we create
    482  * @param token claim token to use, NULL for none
    483  * @param amount how much we asked for
    484  * @return transaction status
    485  */
    486 static enum SYNC_DB_QueryStatus
    487 postgres_store_payment (void *cls,
    488                         const struct SYNC_AccountPublicKeyP *account_pub,
    489                         const char *order_id,
    490                         const struct TALER_ClaimTokenP *token,
    491                         const struct TALER_Amount *amount)
    492 {
    493   struct PostgresClosure *pg = cls;
    494   enum GNUNET_DB_QueryStatus qs;
    495   struct TALER_ClaimTokenP tok;
    496   struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
    497   struct GNUNET_PQ_QueryParam params[] = {
    498     GNUNET_PQ_query_param_auto_from_type (account_pub),
    499     GNUNET_PQ_query_param_string (order_id),
    500     GNUNET_PQ_query_param_auto_from_type (&tok),
    501     GNUNET_PQ_query_param_timestamp (&now),
    502     TALER_PQ_query_param_amount (pg->conn,
    503                                  amount),
    504     GNUNET_PQ_query_param_end
    505   };
    506 
    507   if (NULL == token)
    508     memset (&tok, 0, sizeof (tok));
    509   else
    510     tok = *token;
    511   check_connection (pg);
    512   postgres_preflight (pg);
    513   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    514                                            "payment_insert",
    515                                            params);
    516   switch (qs)
    517   {
    518   case GNUNET_DB_STATUS_SOFT_ERROR:
    519     GNUNET_break (0);
    520     return SYNC_DB_SOFT_ERROR;
    521   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    522     GNUNET_break (0);
    523     return SYNC_DB_NO_RESULTS;
    524   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    525     return SYNC_DB_ONE_RESULT;
    526   case GNUNET_DB_STATUS_HARD_ERROR:
    527     GNUNET_break (0);
    528     return SYNC_DB_HARD_ERROR;
    529   default:
    530     GNUNET_break (0);
    531     return SYNC_DB_HARD_ERROR;
    532   }
    533 }
    534 
    535 
    536 /**
    537  * Closure for #payment_by_account_cb.
    538  */
    539 struct PaymentIteratorContext
    540 {
    541   /**
    542    * Function to call on each result
    543    */
    544   SYNC_DB_PaymentPendingIterator it;
    545 
    546   /**
    547    * Closure for @e it.
    548    */
    549   void *it_cls;
    550 
    551   /**
    552    * Plugin context.
    553    */
    554   struct PostgresClosure *pg;
    555 
    556   /**
    557    * Query status to return.
    558    */
    559   enum GNUNET_DB_QueryStatus qs;
    560 
    561 };
    562 
    563 
    564 /**
    565  * Helper function for #postgres_lookup_pending_payments_by_account().
    566  * To be called with the results of a SELECT statement
    567  * that has returned @a num_results results.
    568  *
    569  * @param cls closure of type `struct PaymentIteratorContext *`
    570  * @param result the postgres result
    571  * @param num_results the number of results in @a result
    572  */
    573 static void
    574 payment_by_account_cb (void *cls,
    575                        PGresult *result,
    576                        unsigned int num_results)
    577 {
    578   struct PaymentIteratorContext *pic = cls;
    579 
    580   for (unsigned int i = 0; i < num_results; i++)
    581   {
    582     struct GNUNET_TIME_Timestamp timestamp;
    583     char *order_id;
    584     struct TALER_Amount amount;
    585     struct TALER_ClaimTokenP token;
    586     struct GNUNET_PQ_ResultSpec rs[] = {
    587       GNUNET_PQ_result_spec_timestamp ("timestamp",
    588                                        &timestamp),
    589       GNUNET_PQ_result_spec_string ("order_id",
    590                                     &order_id),
    591       GNUNET_PQ_result_spec_auto_from_type ("token",
    592                                             &token),
    593       TALER_PQ_result_spec_amount ("amount",
    594                                    pic->pg->currency,
    595                                    &amount),
    596       GNUNET_PQ_result_spec_end
    597     };
    598 
    599     if (GNUNET_OK !=
    600         GNUNET_PQ_extract_result (result,
    601                                   rs,
    602                                   i))
    603     {
    604       GNUNET_break (0);
    605       pic->qs = GNUNET_DB_STATUS_HARD_ERROR;
    606       return;
    607     }
    608     pic->qs = i + 1;
    609     pic->it (pic->it_cls,
    610              timestamp,
    611              order_id,
    612              &token,
    613              &amount);
    614     GNUNET_PQ_cleanup_result (rs);
    615   }
    616 }
    617 
    618 
    619 /**
    620  * Lookup pending payments by account.
    621  *
    622  * @param cls closure
    623  * @param account_pub account to look for pending payments under
    624  * @param it iterator to call on all pending payments
    625  * @param it_cls closure for @a it
    626  * @return transaction status
    627  */
    628 static enum GNUNET_DB_QueryStatus
    629 postgres_lookup_pending_payments_by_account (void *cls,
    630                                              const struct
    631                                              SYNC_AccountPublicKeyP *account_pub
    632                                              ,
    633                                              SYNC_DB_PaymentPendingIterator it,
    634                                              void *it_cls)
    635 {
    636   struct PostgresClosure *pg = cls;
    637   struct GNUNET_PQ_QueryParam params[] = {
    638     GNUNET_PQ_query_param_auto_from_type (account_pub),
    639     GNUNET_PQ_query_param_end
    640   };
    641   struct PaymentIteratorContext pic = {
    642     .it = it,
    643     .it_cls = it_cls,
    644     .pg = pg
    645   };
    646   enum GNUNET_DB_QueryStatus qs;
    647 
    648   check_connection (pg);
    649   postgres_preflight (pg);
    650   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    651                                              "payments_select_by_account",
    652                                              params,
    653                                              &payment_by_account_cb,
    654                                              &pic);
    655   if (qs > 0)
    656     return pic.qs;
    657   GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
    658   return qs;
    659 }
    660 
    661 
    662 /**
    663  * Store backup. Only applicable for the FIRST backup under
    664  * an @a account_pub. Use @e update_backup_TR to update an
    665  * existing backup.
    666  *
    667  * @param cls closure
    668  * @param account_pub account to store @a backup under
    669  * @param account_sig signature affirming storage request
    670  * @param backup_hash hash of @a backup
    671  * @param backup_size number of bytes in @a backup
    672  * @param backup raw data to backup
    673  * @return transaction status
    674  */
    675 static enum SYNC_DB_QueryStatus
    676 postgres_store_backup (void *cls,
    677                        const struct SYNC_AccountPublicKeyP *account_pub,
    678                        const struct SYNC_AccountSignatureP *account_sig,
    679                        const struct GNUNET_HashCode *backup_hash,
    680                        size_t backup_size,
    681                        const void *backup)
    682 {
    683   struct PostgresClosure *pg = cls;
    684   enum GNUNET_DB_QueryStatus qs;
    685   struct GNUNET_HashCode bh;
    686   static struct GNUNET_HashCode no_previous_hash;
    687 
    688   check_connection (pg);
    689   postgres_preflight (pg);
    690   {
    691     struct GNUNET_PQ_QueryParam params[] = {
    692       GNUNET_PQ_query_param_auto_from_type (account_pub),
    693       GNUNET_PQ_query_param_auto_from_type (account_sig),
    694       GNUNET_PQ_query_param_auto_from_type (&no_previous_hash),
    695       GNUNET_PQ_query_param_auto_from_type (backup_hash),
    696       GNUNET_PQ_query_param_fixed_size (backup,
    697                                         backup_size),
    698       GNUNET_PQ_query_param_end
    699     };
    700 
    701     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    702                                              "backup_insert",
    703                                              params);
    704   }
    705   switch (qs)
    706   {
    707   case GNUNET_DB_STATUS_SOFT_ERROR:
    708     GNUNET_break (0);
    709     return SYNC_DB_SOFT_ERROR;
    710   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    711     return SYNC_DB_NO_RESULTS;
    712   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    713     return SYNC_DB_ONE_RESULT;
    714   case GNUNET_DB_STATUS_HARD_ERROR:
    715     /* handle interesting case below */
    716     break;
    717   default:
    718     GNUNET_break (0);
    719     return SYNC_DB_HARD_ERROR;
    720   }
    721 
    722   /* First, check if account exists */
    723   {
    724     struct GNUNET_TIME_Timestamp ed;
    725     struct GNUNET_PQ_QueryParam params[] = {
    726       GNUNET_PQ_query_param_auto_from_type (account_pub),
    727       GNUNET_PQ_query_param_end
    728     };
    729     struct GNUNET_PQ_ResultSpec rs[] = {
    730       GNUNET_PQ_result_spec_auto_from_type ("expiration_date",
    731                                             &ed),
    732       GNUNET_PQ_result_spec_end
    733     };
    734 
    735     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    736                                                    "account_select",
    737                                                    params,
    738                                                    rs);
    739   }
    740   switch (qs)
    741   {
    742   case GNUNET_DB_STATUS_HARD_ERROR:
    743     return SYNC_DB_HARD_ERROR;
    744   case GNUNET_DB_STATUS_SOFT_ERROR:
    745     GNUNET_break (0);
    746     return SYNC_DB_SOFT_ERROR;
    747   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    748     return SYNC_DB_PAYMENT_REQUIRED;
    749   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    750     /* handle interesting case below */
    751     break;
    752   default:
    753     GNUNET_break (0);
    754     return SYNC_DB_HARD_ERROR;
    755   }
    756 
    757   /* account exists, check if existing backup conflicts */
    758   {
    759     struct GNUNET_PQ_QueryParam params[] = {
    760       GNUNET_PQ_query_param_auto_from_type (account_pub),
    761       GNUNET_PQ_query_param_end
    762     };
    763     struct GNUNET_PQ_ResultSpec rs[] = {
    764       GNUNET_PQ_result_spec_auto_from_type ("backup_hash",
    765                                             &bh),
    766       GNUNET_PQ_result_spec_end
    767     };
    768 
    769     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    770                                                    "backup_select_hash",
    771                                                    params,
    772                                                    rs);
    773   }
    774   switch (qs)
    775   {
    776   case GNUNET_DB_STATUS_HARD_ERROR:
    777     return SYNC_DB_HARD_ERROR;
    778   case GNUNET_DB_STATUS_SOFT_ERROR:
    779     GNUNET_break (0);
    780     return SYNC_DB_SOFT_ERROR;
    781   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    782     /* original error must have been a hard error, oddly enough */
    783     return SYNC_DB_HARD_ERROR;
    784   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    785     /* handle interesting case below */
    786     break;
    787   default:
    788     GNUNET_break (0);
    789     return SYNC_DB_HARD_ERROR;
    790   }
    791 
    792   /* had an existing backup, is it identical? */
    793   if (0 != GNUNET_memcmp (&bh,
    794                           backup_hash))
    795     /* previous conflicting backup exists */
    796     return SYNC_DB_OLD_BACKUP_MISMATCH;
    797   /* backup identical to what was provided, no change */
    798   return SYNC_DB_NO_RESULTS;
    799 }
    800 
    801 
    802 /**
    803  * Update backup.
    804  *
    805  * @param cls closure
    806  * @param account_pub account to store @a backup under
    807  * @param account_sig signature affirming storage request
    808  * @param old_backup_hash hash of the previous backup (must match)
    809  * @param backup_hash hash of @a backup
    810  * @param backup_size number of bytes in @a backup
    811  * @param backup raw data to backup
    812  * @return transaction status
    813  */
    814 static enum SYNC_DB_QueryStatus
    815 postgres_update_backup (void *cls,
    816                         const struct SYNC_AccountPublicKeyP *account_pub,
    817                         const struct GNUNET_HashCode *old_backup_hash,
    818                         const struct SYNC_AccountSignatureP *account_sig,
    819                         const struct GNUNET_HashCode *backup_hash,
    820                         size_t backup_size,
    821                         const void *backup)
    822 {
    823   struct PostgresClosure *pg = cls;
    824   enum GNUNET_DB_QueryStatus qs;
    825   struct GNUNET_HashCode bh;
    826 
    827   check_connection (pg);
    828   postgres_preflight (pg);
    829   {
    830     struct GNUNET_PQ_QueryParam params[] = {
    831       GNUNET_PQ_query_param_auto_from_type (backup_hash),
    832       GNUNET_PQ_query_param_auto_from_type (account_sig),
    833       GNUNET_PQ_query_param_auto_from_type (old_backup_hash),
    834       GNUNET_PQ_query_param_fixed_size (backup,
    835                                         backup_size),
    836       GNUNET_PQ_query_param_auto_from_type (account_pub),
    837       GNUNET_PQ_query_param_auto_from_type (old_backup_hash),
    838       GNUNET_PQ_query_param_end
    839     };
    840 
    841     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    842                                              "backup_update",
    843                                              params);
    844   }
    845   switch (qs)
    846   {
    847   case GNUNET_DB_STATUS_SOFT_ERROR:
    848     GNUNET_break (0);
    849     return SYNC_DB_SOFT_ERROR;
    850   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    851     /* handle interesting case below */
    852     break;
    853   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    854     return SYNC_DB_ONE_RESULT;
    855   case GNUNET_DB_STATUS_HARD_ERROR:
    856     GNUNET_break (0);
    857     return SYNC_DB_HARD_ERROR;
    858   default:
    859     GNUNET_break (0);
    860     return SYNC_DB_HARD_ERROR;
    861   }
    862 
    863   /* First, check if account exists */
    864   {
    865     struct GNUNET_TIME_Timestamp ed;
    866     struct GNUNET_PQ_QueryParam params[] = {
    867       GNUNET_PQ_query_param_auto_from_type (account_pub),
    868       GNUNET_PQ_query_param_end
    869     };
    870     struct GNUNET_PQ_ResultSpec rs[] = {
    871       GNUNET_PQ_result_spec_auto_from_type ("expiration_date",
    872                                             &ed),
    873       GNUNET_PQ_result_spec_end
    874     };
    875 
    876     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    877                                                    "account_select",
    878                                                    params,
    879                                                    rs);
    880   }
    881   switch (qs)
    882   {
    883   case GNUNET_DB_STATUS_HARD_ERROR:
    884     return SYNC_DB_HARD_ERROR;
    885   case GNUNET_DB_STATUS_SOFT_ERROR:
    886     GNUNET_break (0);
    887     return SYNC_DB_SOFT_ERROR;
    888   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    889     return SYNC_DB_PAYMENT_REQUIRED;
    890   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    891     /* handle interesting case below */
    892     break;
    893   default:
    894     GNUNET_break (0);
    895     return SYNC_DB_HARD_ERROR;
    896   }
    897 
    898   /* account exists, check if existing backup conflicts */
    899   {
    900     struct GNUNET_PQ_QueryParam params[] = {
    901       GNUNET_PQ_query_param_auto_from_type (account_pub),
    902       GNUNET_PQ_query_param_end
    903     };
    904     struct GNUNET_PQ_ResultSpec rs[] = {
    905       GNUNET_PQ_result_spec_auto_from_type ("backup_hash",
    906                                             &bh),
    907       GNUNET_PQ_result_spec_end
    908     };
    909 
    910     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    911                                                    "backup_select_hash",
    912                                                    params,
    913                                                    rs);
    914   }
    915   switch (qs)
    916   {
    917   case GNUNET_DB_STATUS_HARD_ERROR:
    918     return SYNC_DB_HARD_ERROR;
    919   case GNUNET_DB_STATUS_SOFT_ERROR:
    920     GNUNET_break (0);
    921     return SYNC_DB_SOFT_ERROR;
    922   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    923     return SYNC_DB_OLD_BACKUP_MISSING;
    924   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    925     /* handle interesting case below */
    926     break;
    927   default:
    928     GNUNET_break (0);
    929     return SYNC_DB_HARD_ERROR;
    930   }
    931 
    932   /* had an existing backup, is it identical? */
    933   if (0 == GNUNET_memcmp (&bh,
    934                           backup_hash))
    935   {
    936     /* backup identical to what was provided, no change */
    937     return SYNC_DB_NO_RESULTS;
    938   }
    939   if (0 == GNUNET_memcmp (&bh,
    940                           old_backup_hash))
    941     /* all constraints seem satisfied, original error must
    942        have been a hard error */
    943     return SYNC_DB_HARD_ERROR;
    944   /* previous backup does not match old_backup_hash */
    945   return SYNC_DB_OLD_BACKUP_MISMATCH;
    946 }
    947 
    948 
    949 /**
    950  * Lookup an account and associated backup meta data.
    951  *
    952  * @param cls closure
    953  * @param account_pub account to store @a backup under
    954  * @param[out] backup_hash set to hash of @a backup
    955  * @return transaction status
    956  */
    957 static enum SYNC_DB_QueryStatus
    958 postgres_lookup_account (void *cls,
    959                          const struct SYNC_AccountPublicKeyP *account_pub,
    960                          struct GNUNET_HashCode *backup_hash)
    961 {
    962   struct PostgresClosure *pg = cls;
    963   enum GNUNET_DB_QueryStatus qs;
    964   struct GNUNET_PQ_QueryParam params[] = {
    965     GNUNET_PQ_query_param_auto_from_type (account_pub),
    966     GNUNET_PQ_query_param_end
    967   };
    968 
    969   check_connection (pg);
    970   postgres_preflight (pg);
    971   {
    972     struct GNUNET_PQ_ResultSpec rs[] = {
    973       GNUNET_PQ_result_spec_auto_from_type ("backup_hash",
    974                                             backup_hash),
    975       GNUNET_PQ_result_spec_end
    976     };
    977 
    978     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    979                                                    "backup_select_hash",
    980                                                    params,
    981                                                    rs);
    982   }
    983   switch (qs)
    984   {
    985   case GNUNET_DB_STATUS_HARD_ERROR:
    986     return SYNC_DB_HARD_ERROR;
    987   case GNUNET_DB_STATUS_SOFT_ERROR:
    988     GNUNET_break (0);
    989     return SYNC_DB_SOFT_ERROR;
    990   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    991     break; /* handle interesting case below */
    992   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    993     return SYNC_DB_ONE_RESULT;
    994   default:
    995     GNUNET_break (0);
    996     return SYNC_DB_HARD_ERROR;
    997   }
    998 
    999   /* check if account exists */
   1000   {
   1001     struct GNUNET_TIME_Timestamp expiration;
   1002     struct GNUNET_PQ_ResultSpec rs[] = {
   1003       GNUNET_PQ_result_spec_auto_from_type ("expiration_date",
   1004                                             &expiration),
   1005       GNUNET_PQ_result_spec_end
   1006     };
   1007 
   1008     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   1009                                                    "account_select",
   1010                                                    params,
   1011                                                    rs);
   1012   }
   1013   switch (qs)
   1014   {
   1015   case GNUNET_DB_STATUS_HARD_ERROR:
   1016     return SYNC_DB_HARD_ERROR;
   1017   case GNUNET_DB_STATUS_SOFT_ERROR:
   1018     GNUNET_break (0);
   1019     return SYNC_DB_SOFT_ERROR;
   1020   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1021     /* indicates: no account */
   1022     return SYNC_DB_PAYMENT_REQUIRED;
   1023   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1024     /* indicates: no backup */
   1025     return SYNC_DB_NO_RESULTS;
   1026   default:
   1027     GNUNET_break (0);
   1028     return SYNC_DB_HARD_ERROR;
   1029   }
   1030 }
   1031 
   1032 
   1033 /**
   1034  * Obtain backup.
   1035  *
   1036  * @param cls closure
   1037  * @param account_pub account to store @a backup under
   1038  * @param[out] account_sig set to signature affirming storage request
   1039  * @param[out] prev_hash set to hash of previous @a backup, all zeros if none
   1040  * @param[out] backup_hash set to hash of @a backup
   1041  * @param[out] backup_size set to number of bytes in @a backup
   1042  * @param[out] backup set to raw data to backup, caller MUST FREE
   1043  */
   1044 static enum SYNC_DB_QueryStatus
   1045 postgres_lookup_backup (void *cls,
   1046                         const struct SYNC_AccountPublicKeyP *account_pub,
   1047                         struct SYNC_AccountSignatureP *account_sig,
   1048                         struct GNUNET_HashCode *prev_hash,
   1049                         struct GNUNET_HashCode *backup_hash,
   1050                         size_t *backup_size,
   1051                         void **backup)
   1052 {
   1053   struct PostgresClosure *pg = cls;
   1054   enum GNUNET_DB_QueryStatus qs;
   1055   struct GNUNET_PQ_QueryParam params[] = {
   1056     GNUNET_PQ_query_param_auto_from_type (account_pub),
   1057     GNUNET_PQ_query_param_end
   1058   };
   1059   struct GNUNET_PQ_ResultSpec rs[] = {
   1060     GNUNET_PQ_result_spec_auto_from_type ("account_sig",
   1061                                           account_sig),
   1062     GNUNET_PQ_result_spec_auto_from_type ("prev_hash",
   1063                                           prev_hash),
   1064     GNUNET_PQ_result_spec_auto_from_type ("backup_hash",
   1065                                           backup_hash),
   1066     GNUNET_PQ_result_spec_variable_size ("data",
   1067                                          backup,
   1068                                          backup_size),
   1069     GNUNET_PQ_result_spec_end
   1070   };
   1071 
   1072   check_connection (pg);
   1073   postgres_preflight (pg);
   1074   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   1075                                                  "backup_select",
   1076                                                  params,
   1077                                                  rs);
   1078   switch (qs)
   1079   {
   1080   case GNUNET_DB_STATUS_HARD_ERROR:
   1081     return SYNC_DB_HARD_ERROR;
   1082   case GNUNET_DB_STATUS_SOFT_ERROR:
   1083     GNUNET_break (0);
   1084     return SYNC_DB_SOFT_ERROR;
   1085   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1086     return SYNC_DB_NO_RESULTS;
   1087   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1088     return SYNC_DB_ONE_RESULT;
   1089   default:
   1090     GNUNET_break (0);
   1091     return SYNC_DB_HARD_ERROR;
   1092   }
   1093 }
   1094 
   1095 
   1096 /**
   1097  * Increment account lifetime.
   1098  *
   1099  * @param cls closure
   1100  * @param account_pub which account received a payment
   1101  * @param order_id order which was paid, must be unique and match pending payment
   1102  * @param lifetime for how long is the account now paid (increment)
   1103  * @return transaction status
   1104  */
   1105 static enum SYNC_DB_QueryStatus
   1106 postgres_increment_lifetime (void *cls,
   1107                              const struct SYNC_AccountPublicKeyP *account_pub,
   1108                              const char *order_id,
   1109                              struct GNUNET_TIME_Relative lifetime)
   1110 {
   1111   struct PostgresClosure *pg = cls;
   1112   struct GNUNET_TIME_Timestamp expiration;
   1113   enum GNUNET_DB_QueryStatus qs;
   1114 
   1115   check_connection (pg);
   1116   if (GNUNET_OK !=
   1117       begin_transaction (pg,
   1118                          "increment lifetime"))
   1119   {
   1120     GNUNET_break (0);
   1121     return SYNC_DB_HARD_ERROR;
   1122   }
   1123 
   1124   {
   1125     struct GNUNET_PQ_QueryParam params[] = {
   1126       GNUNET_PQ_query_param_string (order_id),
   1127       GNUNET_PQ_query_param_auto_from_type (account_pub),
   1128       GNUNET_PQ_query_param_end
   1129     };
   1130 
   1131     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
   1132                                              "payment_done",
   1133                                              params);
   1134     switch (qs)
   1135     {
   1136     case GNUNET_DB_STATUS_HARD_ERROR:
   1137       GNUNET_break (0);
   1138       rollback (pg);
   1139       return SYNC_DB_HARD_ERROR;
   1140     case GNUNET_DB_STATUS_SOFT_ERROR:
   1141       GNUNET_break (0);
   1142       rollback (pg);
   1143       return SYNC_DB_SOFT_ERROR;
   1144     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1145       rollback (pg);
   1146       return SYNC_DB_NO_RESULTS;
   1147     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1148       break;
   1149     }
   1150   }
   1151 
   1152   {
   1153     struct GNUNET_PQ_QueryParam params[] = {
   1154       GNUNET_PQ_query_param_auto_from_type (account_pub),
   1155       GNUNET_PQ_query_param_end
   1156     };
   1157     struct GNUNET_PQ_ResultSpec rs[] = {
   1158       GNUNET_PQ_result_spec_timestamp ("expiration_date",
   1159                                        &expiration),
   1160       GNUNET_PQ_result_spec_end
   1161     };
   1162 
   1163     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   1164                                                    "account_select",
   1165                                                    params,
   1166                                                    rs);
   1167   }
   1168 
   1169   switch (qs)
   1170   {
   1171   case GNUNET_DB_STATUS_HARD_ERROR:
   1172     rollback (pg);
   1173     return SYNC_DB_HARD_ERROR;
   1174   case GNUNET_DB_STATUS_SOFT_ERROR:
   1175     rollback (pg);
   1176     return SYNC_DB_SOFT_ERROR;
   1177   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1178     {
   1179       struct GNUNET_PQ_QueryParam params[] = {
   1180         GNUNET_PQ_query_param_auto_from_type (account_pub),
   1181         GNUNET_PQ_query_param_timestamp (&expiration),
   1182         GNUNET_PQ_query_param_end
   1183       };
   1184 
   1185       expiration = GNUNET_TIME_relative_to_timestamp (lifetime);
   1186       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
   1187                                                "account_insert",
   1188                                                params);
   1189     }
   1190     break;
   1191   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1192     {
   1193       struct GNUNET_PQ_QueryParam params[] = {
   1194         GNUNET_PQ_query_param_timestamp (&expiration),
   1195         GNUNET_PQ_query_param_auto_from_type (account_pub),
   1196         GNUNET_PQ_query_param_end
   1197       };
   1198 
   1199       expiration = GNUNET_TIME_absolute_to_timestamp (
   1200         GNUNET_TIME_absolute_add (expiration.abs_time,
   1201                                   lifetime));
   1202       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
   1203                                                "account_update",
   1204                                                params);
   1205     }
   1206     break;
   1207   default:
   1208     GNUNET_break (0);
   1209     return SYNC_DB_HARD_ERROR;
   1210   }
   1211   switch (qs)
   1212   {
   1213   case GNUNET_DB_STATUS_HARD_ERROR:
   1214     rollback (pg);
   1215     return SYNC_DB_HARD_ERROR;
   1216   case GNUNET_DB_STATUS_SOFT_ERROR:
   1217     rollback (pg);
   1218     GNUNET_break (0);
   1219     return SYNC_DB_SOFT_ERROR;
   1220   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1221     GNUNET_break (0);
   1222     rollback (pg);
   1223     return SYNC_DB_NO_RESULTS;
   1224   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1225     break;
   1226   default:
   1227     GNUNET_break (0);
   1228     return SYNC_DB_HARD_ERROR;
   1229   }
   1230   qs = commit_transaction (pg);
   1231   switch (qs)
   1232   {
   1233   case GNUNET_DB_STATUS_HARD_ERROR:
   1234     return SYNC_DB_HARD_ERROR;
   1235   case GNUNET_DB_STATUS_SOFT_ERROR:
   1236     GNUNET_break (0);
   1237     return SYNC_DB_SOFT_ERROR;
   1238   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1239     return SYNC_DB_ONE_RESULT;
   1240   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1241     return SYNC_DB_ONE_RESULT;
   1242   default:
   1243     GNUNET_break (0);
   1244     return SYNC_DB_HARD_ERROR;
   1245   }
   1246 }
   1247 
   1248 
   1249 /**
   1250  * Initialize tables.
   1251  *
   1252  * @param cls the `struct PostgresClosure` with the plugin-specific state
   1253  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
   1254  */
   1255 static enum GNUNET_GenericReturnValue
   1256 postgres_create_tables (void *cls)
   1257 {
   1258   struct PostgresClosure *pc = cls;
   1259   struct GNUNET_PQ_Context *conn;
   1260   struct GNUNET_PQ_ExecuteStatement es[] = {
   1261     GNUNET_PQ_make_execute ("SET search_path TO sync;"),
   1262     GNUNET_PQ_EXECUTE_STATEMENT_END
   1263   };
   1264 
   1265   conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
   1266                                      "syncdb-postgres",
   1267                                      "sync-",
   1268                                      es,
   1269                                      NULL);
   1270   if (NULL == conn)
   1271     return GNUNET_SYSERR;
   1272   GNUNET_PQ_disconnect (conn);
   1273   return GNUNET_OK;
   1274 }
   1275 
   1276 
   1277 /**
   1278  * Initialize Postgres database subsystem.
   1279  *
   1280  * @param cls a configuration instance
   1281  * @return NULL on error, otherwise a `struct TALER_SYNCDB_Plugin`
   1282  */
   1283 void *
   1284 libsync_plugin_db_postgres_init (void *cls);
   1285 
   1286 /* make compiler happy */
   1287 void *
   1288 libsync_plugin_db_postgres_init (void *cls)
   1289 {
   1290   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
   1291   struct PostgresClosure *pg;
   1292   struct SYNC_DatabasePlugin *plugin;
   1293 
   1294   pg = GNUNET_new (struct PostgresClosure);
   1295   pg->cfg = cfg;
   1296   if (GNUNET_OK !=
   1297       GNUNET_CONFIGURATION_get_value_filename (cfg,
   1298                                                "syncdb-postgres",
   1299                                                "SQL_DIR",
   1300                                                &pg->sql_dir))
   1301   {
   1302     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1303                                "syncdb-postgres",
   1304                                "SQL_DIR");
   1305     GNUNET_free (pg);
   1306     return NULL;
   1307   }
   1308   if (GNUNET_OK !=
   1309       GNUNET_CONFIGURATION_get_value_string (cfg,
   1310                                              "sync",
   1311                                              "CURRENCY",
   1312                                              &pg->currency))
   1313   {
   1314     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1315                                "sync",
   1316                                "CURRENCY");
   1317     GNUNET_free (pg->sql_dir);
   1318     GNUNET_free (pg);
   1319     return NULL;
   1320   }
   1321   plugin = GNUNET_new (struct SYNC_DatabasePlugin);
   1322   plugin->cls = pg;
   1323   plugin->create_tables = &postgres_create_tables;
   1324   plugin->drop_tables = &postgres_drop_tables;
   1325   plugin->preflight = &postgres_preflight;
   1326   plugin->gc = &postgres_gc;
   1327   plugin->store_payment_TR = &postgres_store_payment;
   1328   plugin->lookup_pending_payments_by_account_TR =
   1329     &postgres_lookup_pending_payments_by_account;
   1330   plugin->store_backup_TR = &postgres_store_backup;
   1331   plugin->lookup_account_TR = &postgres_lookup_account;
   1332   plugin->lookup_backup_TR = &postgres_lookup_backup;
   1333   plugin->update_backup_TR = &postgres_update_backup;
   1334   plugin->increment_lifetime_TR = &postgres_increment_lifetime;
   1335   return plugin;
   1336 }
   1337 
   1338 
   1339 /**
   1340  * Shutdown Postgres database subsystem.
   1341  *
   1342  * @param cls a `struct SYNC_DB_Plugin`
   1343  * @return NULL (always)
   1344  */
   1345 void *
   1346 libsync_plugin_db_postgres_done (void *cls);
   1347 
   1348 /* make compiler happy */
   1349 void *
   1350 libsync_plugin_db_postgres_done (void *cls)
   1351 {
   1352   struct SYNC_DatabasePlugin *plugin = cls;
   1353   struct PostgresClosure *pg = plugin->cls;
   1354 
   1355   GNUNET_PQ_disconnect (pg->conn);
   1356   GNUNET_free (pg->currency);
   1357   GNUNET_free (pg->sql_dir);
   1358   GNUNET_free (pg);
   1359   GNUNET_free (plugin);
   1360   return NULL;
   1361 }
   1362 
   1363 
   1364 /* end of plugin_syncdb_postgres.c */