LCOV - code coverage report
Current view: top level - syncdb - plugin_syncdb_postgres.c (source / functions) Hit Total Coverage
Test: GNU Taler coverage report Lines: 29 439 6.6 %
Date: 2020-08-15 06:13:13 Functions: 1 17 5.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14