summaryrefslogtreecommitdiff
path: root/src/backenddb/pg_insert_reserve.c
blob: 173d9304524717c761d58c8d558b23c1808be405 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
   This file is part of TALER
   Copyright (C) 2022 Taler Systems SA

   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
   Foundation; either version 3, or (at your option) any later version.

   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along with
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
/**
 * @file backenddb/pg_insert_reserve.c
 * @brief Implementation of the insert_reserve function for Postgres
 * @author Christian Grothoff
 */
#include "platform.h"
#include <taler/taler_error_codes.h>
#include <taler/taler_dbevents.h>
#include <taler/taler_pq_lib.h>
#include "pg_insert_reserve.h"
#include "pg_helper.h"

/**
 * How often do we re-try if we run into a DB serialization error?
 */
#define MAX_RETRIES 3


enum TALER_ErrorCode
TMH_PG_insert_reserve (void *cls,
                       const char *instance_id,
                       const struct TALER_ReservePrivateKeyP *reserve_priv,
                       const struct TALER_ReservePublicKeyP *reserve_pub,
                       const struct TALER_MasterPublicKeyP *master_pub,
                       const char *exchange_url,
                       const struct TALER_Amount *initial_balance,
                       struct GNUNET_TIME_Timestamp expiration)
{
  struct PostgresClosure *pg = cls;
  unsigned int retries;
  enum GNUNET_DB_QueryStatus qs;

  retries = 0;
  check_connection (pg);
  PREPARE (pg,
           "insert_reserve",
           "INSERT INTO merchant_reward_reserves"
           "(reserve_pub"
           ",merchant_serial"
           ",creation_time"
           ",expiration"
           ",merchant_initial_balance"
           ",exchange_initial_balance"
           ",rewards_committed"
           ",rewards_picked_up"
           ")"
           "SELECT $2, merchant_serial, $3, $4, $5, $6, $6, $6"
           " FROM merchant_instances"
           " WHERE merchant_id=$1");
RETRY:
  if (MAX_RETRIES < ++retries)
    return TALER_EC_GENERIC_DB_SOFT_FAILURE;
  if (GNUNET_OK !=
      TMH_PG_start (pg,
                    "insert reserve"))
  {
    GNUNET_break (0);
    return TALER_EC_GENERIC_DB_START_FAILED;
  }

  /* Setup reserve */
  {
    struct TALER_Amount zero;
    struct GNUNET_TIME_Timestamp now;
    struct GNUNET_PQ_QueryParam params[] = {
      GNUNET_PQ_query_param_string (instance_id),
      GNUNET_PQ_query_param_auto_from_type (reserve_pub),
      GNUNET_PQ_query_param_timestamp (&now),
      GNUNET_PQ_query_param_timestamp (&expiration),
      TALER_PQ_query_param_amount_with_currency (pg->conn,
                                                 initial_balance),
      TALER_PQ_query_param_amount_with_currency (pg->conn,
                                                 &zero),
      GNUNET_PQ_query_param_end
    };

    now = GNUNET_TIME_timestamp_get ();
    GNUNET_assert (GNUNET_OK ==
                   TALER_amount_set_zero (initial_balance->currency,
                                          &zero));
    qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
                                             "insert_reserve",
                                             params);
    if (0 > qs)
    {
      TMH_PG_rollback (pg);
      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
        goto RETRY;
      return qs;
    }
  }
  /* Store private key */
  {
    struct GNUNET_PQ_QueryParam params[] = {
      GNUNET_PQ_query_param_string (instance_id),
      GNUNET_PQ_query_param_auto_from_type (reserve_pub),
      GNUNET_PQ_query_param_auto_from_type (reserve_priv),
      GNUNET_PQ_query_param_string (exchange_url),
      GNUNET_PQ_query_param_auto_from_type (master_pub),
      GNUNET_PQ_query_param_end
    };

    PREPARE(pg,
            "insert_reserve_key",
            "INSERT INTO merchant_reward_reserve_keys"
            "(reserve_serial"
            ",reserve_priv"
            ",exchange_url"
            ",master_pub"
            ")"
            "SELECT reserve_serial, $3, $4, $5"
            " FROM merchant_reward_reserves"
            " WHERE reserve_pub=$2"
            "   AND merchant_serial="
            "     (SELECT merchant_serial"
            "        FROM merchant_instances"
            "       WHERE merchant_id=$1)");

    qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
                                             "insert_reserve_key",
                                             params);
    if (0 > qs)
    {
      TMH_PG_rollback (pg);
      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
        goto RETRY;
      return qs;
    }
  }
  qs = TMH_PG_commit (pg);
  if (0 <= qs)
    return TALER_EC_NONE; /* success  */
  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    goto RETRY;
  return qs;
}