Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 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 General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file helper.h
18 : * @brief shared internal definitions for the Postgres exchange database
19 : * @author Christian Grothoff
20 : */
21 : #ifndef PG_HELPER_H
22 : #define PG_HELPER_H
23 :
24 : #include "taler/taler_dbevents.h" /* UNNECESSARY? */
25 : #include "taler/taler_error_codes.h" /* UNNECESSARY? */
26 : #include "exchangedb_lib.h"
27 : #include "taler/taler_json_lib.h"
28 : #include "taler/taler_pq_lib.h"
29 : #include "taler/taler_util.h"
30 :
31 :
32 : /**
33 : * Context for a Postgres database connection for the exchange.
34 : */
35 : struct TALER_EXCHANGEDB_PostgresContext
36 : {
37 :
38 : /**
39 : * Our configuration.
40 : */
41 : const struct GNUNET_CONFIGURATION_Handle *cfg;
42 :
43 : /**
44 : * Directory with SQL statements to run to create tables.
45 : */
46 : char *sql_dir;
47 :
48 : /**
49 : * How long are AML programs allowed to run?
50 : */
51 : struct GNUNET_TIME_Relative max_aml_program_runtime;
52 :
53 : /**
54 : * After how long should idle reserves be closed?
55 : */
56 : struct GNUNET_TIME_Relative idle_reserve_expiration_time;
57 :
58 : /**
59 : * After how long should reserves that have seen withdraw operations
60 : * be garbage collected?
61 : */
62 : struct GNUNET_TIME_Relative legal_reserve_expiration_time;
63 :
64 : /**
65 : * What delay should we introduce before ready transactions
66 : * are actually aggregated?
67 : */
68 : struct GNUNET_TIME_Relative aggregator_shift;
69 :
70 : /**
71 : * Which currency should we assume all amounts to be in?
72 : */
73 : char *currency;
74 :
75 : /**
76 : * Our base URL.
77 : */
78 : char *exchange_url;
79 :
80 : /**
81 : * Postgres connection handle.
82 : */
83 : struct GNUNET_PQ_Context *conn;
84 :
85 : /**
86 : * Name of the current transaction, for debugging.
87 : */
88 : const char *transaction_name;
89 :
90 : /**
91 : * Number of purses we allow to be opened concurrently
92 : * for one year per annual fee payment.
93 : */
94 : uint32_t def_purse_limit;
95 :
96 : };
97 :
98 :
99 : /**
100 : * Counts how often we have established a fresh @e conn
101 : * to the database. Used to re-prepare statements.
102 : */
103 : extern unsigned long long TEH_PG_prep_gen_;
104 :
105 :
106 : /**
107 : * Convert the signed @a limit of a paginated listing into the row
108 : * limit to pass to SQL. The sign selects the iteration direction and
109 : * the magnitude the number of rows, but computing `-limit` directly is
110 : * undefined behaviour for `INT64_MIN`, and the resulting value would
111 : * reach Postgres as `LIMIT -9223372036854775808`, which errors out.
112 : * `INT64_MIN` is therefore saturated to `INT64_MAX`.
113 : *
114 : * @param limit signed limit as given by the caller
115 : * @return absolute value of @a limit, without signed overflow
116 : */
117 : static inline uint64_t
118 152 : TALER_EXCHANGEDB_abs_limit (int64_t limit)
119 : {
120 : /* Postgres binds INT8 as a *signed* 64-bit integer, so a magnitude
121 : above INT64_MAX would reach LIMIT as a negative number. */
122 152 : if (INT64_MIN == limit)
123 0 : return (uint64_t) INT64_MAX;
124 152 : if (limit < 0)
125 46 : return (uint64_t) -limit;
126 106 : return (uint64_t) limit;
127 : }
128 :
129 :
130 : /**
131 : * Prepares SQL statement @a sql under @a name for
132 : * connection @a pg once.
133 : * Returns with #GNUNET_DB_STATUS_HARD_ERROR on failure.
134 : *
135 : * @param pg a `struct TALER_EXCHANGEDB_PostgresContext`
136 : * @param name name to prepare the statement under
137 : * @param sql actual SQL text
138 : */
139 : #define PREPARE(pg,name,sql) \
140 : do { \
141 : static struct { \
142 : unsigned long long cnt; \
143 : struct TALER_EXCHANGEDB_PostgresContext *pg; \
144 : } preps_[2]; /* 2 ctrs for taler-auditor-sync*/ \
145 : unsigned int off_ = 0; \
146 : \
147 : while ( (off_ < sizeof(preps_) / sizeof(*preps_)) && \
148 : (NULL != preps_[off_].pg) && \
149 : (pg != preps_[off_].pg) ) { \
150 : off_++; \
151 : } \
152 : GNUNET_assert (off_ < \
153 : sizeof(preps_) / sizeof(*preps_)); \
154 : if (preps_[off_].cnt < TEH_PG_prep_gen_) \
155 : { \
156 : struct GNUNET_PQ_PreparedStatement ps[] = { \
157 : GNUNET_PQ_make_prepare (name, sql), \
158 : GNUNET_PQ_PREPARED_STATEMENT_END \
159 : }; \
160 : \
161 : if (GNUNET_OK != \
162 : GNUNET_PQ_prepare_statements (pg->conn, \
163 : ps)) \
164 : { \
165 : GNUNET_break (0); \
166 : return GNUNET_DB_STATUS_HARD_ERROR; \
167 : } \
168 : preps_[off_].pg = pg; \
169 : preps_[off_].cnt = TEH_PG_prep_gen_; \
170 : } \
171 : } while (0)
172 :
173 :
174 : /**
175 : * Wrapper macro to add the currency from the database context in
176 : * the `pg` variable of the calling scope when fetching amounts from
177 : * the database.
178 : *
179 : * @param field name of the database field to fetch amount from
180 : * @param[out] amountp pointer to amount to set
181 : */
182 : #define TALER_PQ_RESULT_SPEC_AMOUNT(field, \
183 : amountp) TALER_PQ_result_spec_amount ( \
184 : field,pg->currency,amountp)
185 :
186 :
187 : #endif
|