tighten formatting rules
[oweals/gnunet.git] / src / pq / pq_connect.c
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_connect.c
22  * @brief functions to connect to libpq (PostGres)
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "pq.h"
27
28
29 /**
30  * Function called by libpq whenever it wants to log something.
31  * We already log whenever we care, so this function does nothing
32  * and merely exists to silence the libpq logging.
33  *
34  * @param arg the SQL connection that was used
35  * @param res information about some libpq event
36  */
37 static void
38 pq_notice_receiver_cb (void *arg,
39                        const PGresult *res)
40 {
41   /* do nothing, intentionally */
42   (void) arg;
43   (void) res;
44 }
45
46
47 /**
48  * Function called by libpq whenever it wants to log something.
49  * We log those using the GNUnet logger.
50  *
51  * @param arg the SQL connection that was used
52  * @param message information about some libpq event
53  */
54 static void
55 pq_notice_processor_cb (void *arg,
56                         const char *message)
57 {
58   (void) arg;
59   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
60                    "pq",
61                    "%s",
62                    message);
63 }
64
65
66 /**
67  * Create a connection to the Postgres database using @a config_str
68  * for the configuration.  Initialize logging via GNUnet's log
69  * routines and disable Postgres's logger.  Also ensures that the
70  * statements in @a es are executed whenever we (re)connect to the
71  * database, and that the prepared statements in @a ps are "ready".
72  * If statements in @es fail that were created with
73  * #GNUNET_PQ_make_execute(), then the entire operation fails.
74  *
75  * The caller MUST ensure that @a es and @a ps remain allocated and
76  * initialized in memory until #GNUNET_PQ_disconnect() is called,
77  * as they may be needed repeatedly and no copy will be made.
78  *
79  * @param config_str configuration to use
80  * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated
81  *            array of statements to execute upon EACH connection, can be NULL
82  * @param ps array of prepared statements to prepare, can be NULL
83  * @return NULL on error
84  */
85 struct GNUNET_PQ_Context *
86 GNUNET_PQ_connect (const char *config_str,
87                    const struct GNUNET_PQ_ExecuteStatement *es,
88                    const struct GNUNET_PQ_PreparedStatement *ps)
89 {
90   struct GNUNET_PQ_Context *db;
91   unsigned int elen = 0;
92   unsigned int plen = 0;
93
94   if (NULL != es)
95     while (NULL != es[elen].sql)
96       elen++;
97   if (NULL != ps)
98     while (NULL != ps[plen].name)
99       plen++;
100
101   db = GNUNET_new (struct GNUNET_PQ_Context);
102   db->config_str = GNUNET_strdup (config_str);
103   if (0 != elen)
104   {
105     db->es = GNUNET_new_array (elen + 1,
106                                struct GNUNET_PQ_ExecuteStatement);
107     memcpy (db->es,
108             es,
109             elen * sizeof (struct GNUNET_PQ_ExecuteStatement));
110   }
111   if (0 != plen)
112   {
113     db->ps = GNUNET_new_array (plen + 1,
114                                struct GNUNET_PQ_PreparedStatement);
115     memcpy (db->ps,
116             ps,
117             plen * sizeof (struct GNUNET_PQ_PreparedStatement));
118   }
119   GNUNET_PQ_reconnect (db);
120   if (NULL == db->conn)
121   {
122     GNUNET_free (db->config_str);
123     GNUNET_free (db);
124     return NULL;
125   }
126   return db;
127 }
128
129
130 /**
131  * Reinitialize the database @a db if the connection is down.
132  *
133  * @param db database connection to reinitialize
134  */
135 void
136 GNUNET_PQ_reconnect_if_down (struct GNUNET_PQ_Context *db)
137 {
138   if (CONNECTION_BAD != PQstatus (db->conn))
139     return;
140   GNUNET_PQ_reconnect (db);
141 }
142
143
144 /**
145  * Reinitialize the database @a db.
146  *
147  * @param db database connection to reinitialize
148  */
149 void
150 GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db)
151 {
152   if (NULL != db->conn)
153     PQfinish (db->conn);
154   db->conn = PQconnectdb (db->config_str);
155   if ((NULL == db->conn) ||
156       (CONNECTION_OK != PQstatus (db->conn)))
157   {
158     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
159                      "pq",
160                      "Database connection to '%s' failed: %s\n",
161                      db->config_str,
162                      (NULL != db->conn) ?
163                      PQerrorMessage (db->conn)
164                      : "PQconnectdb returned NULL");
165     if (NULL != db->conn)
166     {
167       PQfinish (db->conn);
168       db->conn = NULL;
169     }
170     return;
171   }
172   PQsetNoticeReceiver (db->conn,
173                        &pq_notice_receiver_cb,
174                        db);
175   PQsetNoticeProcessor (db->conn,
176                         &pq_notice_processor_cb,
177                         db);
178   if ( (NULL != db->es) &&
179        (GNUNET_OK !=
180         GNUNET_PQ_exec_statements (db,
181                                    db->es)) )
182   {
183     PQfinish (db->conn);
184     db->conn = NULL;
185     return;
186   }
187   if ( (NULL != db->ps) &&
188        (GNUNET_OK !=
189         GNUNET_PQ_prepare_statements (db,
190                                       db->ps)) )
191   {
192     PQfinish (db->conn);
193     db->conn = NULL;
194     return;
195   }
196 }
197
198
199 /**
200  * Connect to a postgres database using the configuration
201  * option "CONFIG" in @a section.  Also ensures that the
202  * statements in @a es are executed whenever we (re)connect to the
203  * database, and that the prepared statements in @a ps are "ready".
204  *
205  * The caller MUST ensure that @a es and @a ps remain allocated and
206  * initialized in memory until #GNUNET_PQ_disconnect() is called,
207  * as they may be needed repeatedly and no copy will be made.
208  *
209  * @param cfg configuration
210  * @param section configuration section to use to get Postgres configuration options
211  * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated
212  *            array of statements to execute upon EACH connection, can be NULL
213  * @param ps array of prepared statements to prepare, can be NULL
214  * @return the postgres handle, NULL on error
215  */
216 struct GNUNET_PQ_Context *
217 GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
218                             const char *section,
219                             const struct GNUNET_PQ_ExecuteStatement *es,
220                             const struct GNUNET_PQ_PreparedStatement *ps)
221 {
222   struct GNUNET_PQ_Context *db;
223   char *conninfo;
224
225   if (GNUNET_OK !=
226       GNUNET_CONFIGURATION_get_value_string (cfg,
227                                              section,
228                                              "CONFIG",
229                                              &conninfo))
230     conninfo = NULL;
231   db = GNUNET_PQ_connect (conninfo == NULL ? "" : conninfo,
232                           es,
233                           ps);
234   GNUNET_free_non_null (conninfo);
235   return db;
236 }
237
238
239 /**
240  * Disconnect from the database, destroying the prepared statements
241  * and releasing other associated resources.
242  *
243  * @param db database handle to disconnect (will be free'd)
244  */
245 void
246 GNUNET_PQ_disconnect (struct GNUNET_PQ_Context *db)
247 {
248   GNUNET_free_non_null (db->es);
249   GNUNET_free_non_null (db->ps);
250   PQfinish (db->conn);
251   GNUNET_free (db);
252 }
253
254
255 /* end of pq/pq_connect.c */