aboutsummaryrefslogtreecommitdiff
path: root/src/pq/pq_prepare.c
blob: fcf1cc79330daf44b0e4a6e69b598d8687445190 (plain) (blame)
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
/*
   This file is part of GNUnet
   Copyright (C) 2017, 2019 GNUnet e.V.

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

   GNUnet 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
   Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

     SPDX-License-Identifier: AGPL3.0-or-later
 */
/**
 * @file pq/pq_prepare.c
 * @brief functions to connect to libpq (PostGres)
 * @author Christian Grothoff
 */
#include "platform.h"
#include "pq.h"


/**
 * Create a `struct GNUNET_PQ_PreparedStatement`.
 *
 * @param name name of the statement
 * @param sql actual SQL statement
 * @param num_args number of arguments in the statement
 * @return initialized struct
 */
struct GNUNET_PQ_PreparedStatement
GNUNET_PQ_make_prepare (const char *name,
                        const char *sql,
                        unsigned int num_args)
{
  struct GNUNET_PQ_PreparedStatement ps = {
    .name = name,
    .sql = sql,
    .num_arguments = num_args
  };

  return ps;
}


/**
 * Request creation of prepared statements @a ps from Postgres.
 *
 * @param db database to prepare the statements for
 * @param ps #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
 *            statements.
 * @return #GNUNET_OK on success,
 *         #GNUNET_SYSERR on error
 */
enum GNUNET_GenericReturnValue
GNUNET_PQ_prepare_statements (struct GNUNET_PQ_Context *db,
                              const struct GNUNET_PQ_PreparedStatement *ps)
{
  if (db->ps != ps)
  {
    /* add 'ps' to list db->ps of prepared statements to run on reconnect! */
    unsigned int olen = 0; /* length of existing 'db->ps' array */
    unsigned int nlen = 0; /* length of 'ps' array */
    struct GNUNET_PQ_PreparedStatement *rps; /* combined array */

    if (NULL != db->ps)
      while (NULL != db->ps[olen].name)
        olen++;
    while (NULL != ps[nlen].name)
      nlen++;
    rps = GNUNET_new_array (olen + nlen + 1,
                            struct GNUNET_PQ_PreparedStatement);
    if (NULL != db->ps)
      memcpy (rps,
              db->ps,
              olen * sizeof (struct GNUNET_PQ_PreparedStatement));
    memcpy (&rps[olen],
            ps,
            nlen * sizeof (struct GNUNET_PQ_PreparedStatement));
    GNUNET_free (db->ps);
    db->ps = rps;
  }

  /* actually prepare statements */
  for (unsigned int i = 0; NULL != ps[i].name; i++)
  {
    PGresult *ret;

    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
                     "pq",
                     "Preparing SQL statement `%s' as `%s'\n",
                     ps[i].sql,
                     ps[i].name);
    ret = PQprepare (db->conn,
                     ps[i].name,
                     ps[i].sql,
                     ps[i].num_arguments,
                     NULL);
    if (PGRES_COMMAND_OK != PQresultStatus (ret))
    {
      GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
                       "pq",
                       "PQprepare (`%s' as `%s') failed with error: %s\n",
                       ps[i].sql,
                       ps[i].name,
                       PQerrorMessage (db->conn));
      PQclear (ret);
      ret = PQdescribePrepared (db->conn,
                                ps[i].name);
      if (PGRES_COMMAND_OK != PQresultStatus (ret))
      {
        PQclear (ret);
        return GNUNET_SYSERR;
      }
      GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
                       "pq",
                       "Statement `%s' already known. Ignoring the issue in the hope that you are using connection pooling...\n",
                       ps[i].name);
    }
    PQclear (ret);
  }
  return GNUNET_OK;
}


/* end of pq/pq_prepare.c */