aboutsummaryrefslogtreecommitdiff
path: root/src/lib/pq/pq_eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/pq/pq_eval.c')
-rw-r--r--src/lib/pq/pq_eval.c248
1 files changed, 248 insertions, 0 deletions
diff --git a/src/lib/pq/pq_eval.c b/src/lib/pq/pq_eval.c
new file mode 100644
index 000000000..e31475e13
--- /dev/null
+++ b/src/lib/pq/pq_eval.c
@@ -0,0 +1,248 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017, 2019 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file pq/pq_eval.c
22 * @brief functions to execute SQL statements with arguments and/or results (PostGres)
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "pq.h"
27
28
29/**
30 * Error code returned by Postgres for deadlock.
31 */
32#define PQ_DIAG_SQLSTATE_DEADLOCK "40P01"
33
34/**
35 * Error code returned by Postgres for uniqueness violation.
36 */
37#define PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION "23505"
38
39/**
40 * Error code returned by Postgres on serialization failure.
41 */
42#define PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE "40001"
43
44
45enum GNUNET_DB_QueryStatus
46GNUNET_PQ_eval_result (struct GNUNET_PQ_Context *db,
47 const char *statement_name,
48 PGresult *result)
49{
50 ExecStatusType est;
51
52 if (NULL == result)
53 return GNUNET_DB_STATUS_SOFT_ERROR;
54 est = PQresultStatus (result);
55 if ((PGRES_COMMAND_OK != est) &&
56 (PGRES_TUPLES_OK != est))
57 {
58 const char *sqlstate;
59 ConnStatusType status;
60
61 if (CONNECTION_OK != (status = PQstatus (db->conn)))
62 {
63 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
64 "pq",
65 "Database connection failed during query `%s': %d (reconnecting)\n",
66 statement_name,
67 status);
68 GNUNET_PQ_reconnect (db);
69 return GNUNET_DB_STATUS_SOFT_ERROR;
70 }
71
72 sqlstate = PQresultErrorField (result,
73 PG_DIAG_SQLSTATE);
74 if (NULL == sqlstate)
75 {
76 /* very unexpected... */
77 GNUNET_break (0);
78 return GNUNET_DB_STATUS_HARD_ERROR;
79 }
80 if ((0 == strcmp (sqlstate,
81 PQ_DIAG_SQLSTATE_DEADLOCK)) ||
82 (0 == strcmp (sqlstate,
83 PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE)))
84 {
85 /* These two can be retried and have a fair chance of working
86 the next time */
87 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
88 "pq",
89 "Query `%s' failed with result: %s/%s/%s/%s/%s\n",
90 statement_name,
91 PQresultErrorField (result,
92 PG_DIAG_MESSAGE_PRIMARY),
93 PQresultErrorField (result,
94 PG_DIAG_MESSAGE_DETAIL),
95 PQresultErrorMessage (result),
96 PQresStatus (PQresultStatus (result)),
97 PQerrorMessage (db->conn));
98 return GNUNET_DB_STATUS_SOFT_ERROR;
99 }
100 if (0 == strcmp (sqlstate,
101 PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION))
102 {
103 /* Likely no need to retry, INSERT of "same" data. */
104 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
105 "pq",
106 "Query `%s' failed with unique violation: %s/%s/%s/%s/%s\n",
107 statement_name,
108 PQresultErrorField (result,
109 PG_DIAG_MESSAGE_PRIMARY),
110 PQresultErrorField (result,
111 PG_DIAG_MESSAGE_DETAIL),
112 PQresultErrorMessage (result),
113 PQresStatus (PQresultStatus (result)),
114 PQerrorMessage (db->conn));
115 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
116 }
117 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
118 "pq",
119 "Query `%s' failed with result: %s/%s/%s/%s/%s\n",
120 statement_name,
121 PQresultErrorField (result,
122 PG_DIAG_MESSAGE_PRIMARY),
123 PQresultErrorField (result,
124 PG_DIAG_MESSAGE_DETAIL),
125 PQresultErrorMessage (result),
126 PQresStatus (PQresultStatus (result)),
127 PQerrorMessage (db->conn));
128 return GNUNET_DB_STATUS_HARD_ERROR;
129 }
130 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
131}
132
133
134enum GNUNET_DB_QueryStatus
135GNUNET_PQ_eval_prepared_non_select (struct GNUNET_PQ_Context *db,
136 const char *statement_name,
137 const struct GNUNET_PQ_QueryParam *params)
138{
139 PGresult *result;
140 enum GNUNET_DB_QueryStatus qs;
141
142 result = GNUNET_PQ_exec_prepared (db,
143 statement_name,
144 params);
145 if (NULL == result)
146 return GNUNET_DB_STATUS_SOFT_ERROR;
147 qs = GNUNET_PQ_eval_result (db,
148 statement_name,
149 result);
150 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
151 {
152 const char *tuples;
153
154 /* What an awful API, this function really does return a string */
155 tuples = PQcmdTuples (result);
156 if (NULL != tuples)
157 qs = strtol (tuples, NULL, 10);
158 }
159 PQclear (result);
160 return qs;
161}
162
163
164enum GNUNET_DB_QueryStatus
165GNUNET_PQ_eval_prepared_multi_select (struct GNUNET_PQ_Context *db,
166 const char *statement_name,
167 const struct GNUNET_PQ_QueryParam *params,
168 GNUNET_PQ_PostgresResultHandler rh,
169 void *rh_cls)
170{
171 PGresult *result;
172 enum GNUNET_DB_QueryStatus qs;
173 unsigned int ret;
174
175 result = GNUNET_PQ_exec_prepared (db,
176 statement_name,
177 params);
178 if (NULL == result)
179 return GNUNET_DB_STATUS_SOFT_ERROR;
180 qs = GNUNET_PQ_eval_result (db,
181 statement_name,
182 result);
183 if (qs < 0)
184 {
185 PQclear (result);
186 return qs;
187 }
188 ret = PQntuples (result);
189 if (NULL != rh)
190 rh (rh_cls,
191 result,
192 ret);
193 PQclear (result);
194 return ret;
195}
196
197
198enum GNUNET_DB_QueryStatus
199GNUNET_PQ_eval_prepared_singleton_select (
200 struct GNUNET_PQ_Context *db,
201 const char *statement_name,
202 const struct GNUNET_PQ_QueryParam *params,
203 struct GNUNET_PQ_ResultSpec *rs)
204{
205 PGresult *result;
206 enum GNUNET_DB_QueryStatus qs;
207 int ntuples;
208
209 result = GNUNET_PQ_exec_prepared (db,
210 statement_name,
211 params);
212 if (NULL == result)
213 return GNUNET_DB_STATUS_SOFT_ERROR;
214 qs = GNUNET_PQ_eval_result (db,
215 statement_name,
216 result);
217 if (qs < 0)
218 {
219 PQclear (result);
220 return qs;
221 }
222 ntuples = PQntuples (result);
223 if (0 == ntuples)
224 {
225 PQclear (result);
226 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
227 }
228 if (1 != ntuples)
229 {
230 /* more than one result, but there must be at most one */
231 GNUNET_break (0);
232 PQclear (result);
233 return GNUNET_DB_STATUS_HARD_ERROR;
234 }
235 if (GNUNET_OK !=
236 GNUNET_PQ_extract_result (result,
237 rs,
238 0))
239 {
240 PQclear (result);
241 return GNUNET_DB_STATUS_HARD_ERROR;
242 }
243 PQclear (result);
244 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
245}
246
247
248/* end of pq/pq_eval.c */