• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 25922098654

15 May 2026 02:03PM UTC coverage: 66.967% (+0.1%) from 66.847%
25922098654

push

github

web-flow
Merge pull request #17378 from rgacogne/ddist-smt-top-counters

dnsdist: Fix the dynamic block top suffixes counters computation

42840 of 81078 branches covered (52.84%)

Branch coverage included in aggregate %.

0 of 2 new or added lines in 1 file covered. (0.0%)

53 existing lines in 12 files now uncovered.

125244 of 169918 relevant lines covered (73.71%)

5898400.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.33
/modules/gpgsqlbackend/spgsql.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22

23
#ifdef HAVE_CONFIG_H
24
#include "config.h"
25
#endif
26
#include <string>
27
#include "spgsql.hh"
28
#include <sys/time.h>
29
#include <iostream>
30
#include "pdns/logger.hh"
31
#include "pdns/dns.hh"
32
#include "pdns/namespaces.hh"
33
#include <algorithm>
34

35
class SPgSQLStatement : public SSqlStatement
36
{
37
public:
38
  SPgSQLStatement(Logr::log_t log, const string& query, bool dolog, int nparams, SPgSQL* db, unsigned int nstatement)
39
  {
131,302✔
40
    d_slog = log;
131,302✔
41
    d_query = query;
131,302✔
42
    d_dolog = dolog;
131,302✔
43
    d_parent = db;
131,302✔
44
    d_nparams = nparams;
131,302✔
45
    d_nstatement = nstatement;
131,302✔
46
  }
131,302✔
47

48
  SSqlStatement* bind(const string& name, bool value) override { return bind(name, string(value ? "t" : "f")); }
529,644✔
49
  SSqlStatement* bind(const string& name, int value) override { return bind(name, std::to_string(value)); }
548,132✔
50
  SSqlStatement* bind(const string& name, uint32_t value) override { return bind(name, std::to_string(value)); }
223,901✔
51
  SSqlStatement* bind(const string& name, long value) override { return bind(name, std::to_string(value)); }
82✔
52
  SSqlStatement* bind(const string& name, unsigned long value) override { return bind(name, std::to_string(value)); }
11✔
53
  SSqlStatement* bind(const string& name, long long value) override { return bind(name, std::to_string(value)); }
×
54
  SSqlStatement* bind(const string& name, unsigned long long value) override { return bind(name, std::to_string(value)); }
×
55
  SSqlStatement* bind(const string& /* name */, const std::string& value) override
56
  {
2,229,216✔
57
    prepareStatement();
2,229,216✔
58
    allocate();
2,229,216✔
59
    if (d_paridx >= d_nparams) {
2,229,216!
60
      releaseStatement();
×
61
      throw SSqlException("Attempt to bind more parameters than query has: " + d_query);
×
62
    }
×
63
    paramValues[d_paridx] = new char[value.size() + 1];
2,229,216✔
64
    memset(paramValues[d_paridx], 0, sizeof(char) * (value.size() + 1));
2,229,216✔
65
    value.copy(paramValues[d_paridx], value.size());
2,229,216✔
66
    paramLengths[d_paridx] = value.size();
2,229,216✔
67
    d_paridx++;
2,229,216✔
68
    return this;
2,229,216✔
69
  }
2,229,216✔
70
  SSqlStatement* bindNull(const string& /* name */) override
71
  {
162,993✔
72
    prepareStatement();
162,993✔
73
    d_paridx++;
162,993✔
74
    return this;
162,993✔
75
  } // these are set null in allocate()
162,993✔
76
  SSqlStatement* execute() override
77
  {
332,326✔
78
    prepareStatement();
332,326✔
79
    if (d_dolog) {
332,326!
80
      if (g_slogStructured) {
×
81
        if (d_paridx) {
×
82
          // This is ugly, but will do until paramValues is converted to a
83
          // std::array.
84
          std::vector<char*> vecparam;
×
85
          vecparam.reserve(d_paridx);
×
86
          for (auto i = 0; i < d_paridx; ++i) {
×
87
            vecparam[i] = paramValues[i];
×
88
          }
×
89
          d_slog->info(Logr::Warning, "execute SQL query", "query", Logging::Loggable(d_query), "arguments", Logging::IterLoggable(vecparam.cbegin(), vecparam.cend()));
×
90
        }
×
91
        else {
×
92
          d_slog->info(Logr::Warning, "execute SQL query", "query", Logging::Loggable(d_query));
×
93
        }
×
94
      }
×
95
      else {
×
96
        g_log << Logger::Warning << "Query " << ((long)(void*)this) << ": Statement: " << d_query << endl;
×
97
        if (d_paridx) {
×
98
          std::stringstream log_message;
×
99
          // Log message is similar, but not exactly the same as the postgres server log.
100
          log_message << "Query " << ((long)(void*)this) << ": Parameters: ";
×
101
          for (int i = 0; i < d_paridx; i++) {
×
102
            if (i != 0) {
×
103
              log_message << ", ";
×
104
            }
×
105
            log_message << "$" << (i + 1) << " = ";
×
106
            if (paramValues[i] == nullptr) {
×
107
              log_message << "NULL";
×
108
            }
×
109
            else {
×
110
              log_message << "'" << paramValues[i] << "'";
×
111
            }
×
112
          }
×
113
          g_log << Logger::Warning << log_message.str() << endl;
×
114
        }
×
115
      }
×
116
      d_dtime.set();
×
117
    }
×
118
    if (!d_stmt.empty()) {
332,326!
119
      d_res_set = PQexecPrepared(d_db(), d_stmt.c_str(), d_nparams, paramValues, paramLengths, nullptr, 0);
332,326✔
120
    }
332,326✔
UNCOV
121
    else {
×
UNCOV
122
      d_res_set = PQexecParams(d_db(), d_query.c_str(), d_nparams, nullptr, paramValues, paramLengths, nullptr, 0);
×
UNCOV
123
    }
×
124
    ExecStatusType status = PQresultStatus(d_res_set);
332,326✔
125
    if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_NONFATAL_ERROR) {
332,326!
126
      string errmsg(PQresultErrorMessage(d_res_set));
×
127
      releaseStatement();
×
128
      throw SSqlException("Fatal error during query: " + d_query + string(": ") + errmsg);
×
129
    }
×
130
    d_cur_set = 0;
332,326✔
131
    if (d_dolog) {
332,326!
132
      auto diff = d_dtime.udiffNoReset();
×
133
      SLOG(g_log << Logger::Warning << "Query " << ((long)(void*)this) << ": " << diff << " us to execute" << endl,
×
134
           d_slog->info(Logr::Warning, "query completed", "microseconds", Logging::Loggable(diff)));
×
135
    }
×
136

137
    nextResult();
332,326✔
138
    return this;
332,326✔
139
  }
332,326✔
140

141
  void nextResult()
142
  {
350,849✔
143
    if (d_res_set == nullptr)
350,849✔
144
      return;
18,526✔
145
    if (d_cur_set >= PQntuples(d_res_set)) {
332,323✔
146
      PQclear(d_res_set);
313,800✔
147
      d_res_set = nullptr;
313,800✔
148
      return;
313,800✔
149
    }
313,800✔
150
    if (PQftype(d_res_set, 0) == 1790) { // REFCURSOR
18,523!
151
      SLOG(g_log << Logger::Error << "Postgres query returned a REFCURSOR and we do not support those - see https://github.com/PowerDNS/pdns/pull/10259" << endl,
×
152
           d_slog->info(Logr::Error, "query returned a REFCURSOR which is not supported by PowerDNS", "more information", Logging::Loggable("https://github.com/PowerDNS/pdns/pull/10259")));
×
153
      PQclear(d_res_set);
×
154
      d_res_set = nullptr;
×
155
    }
×
156
    else {
18,523✔
157
      d_res = d_res_set;
18,523✔
158
      d_res_set = nullptr;
18,523✔
159
      d_resnum = PQntuples(d_res);
18,523✔
160
    }
18,523✔
161
  }
18,523✔
162

163
  bool hasNextRow() override
164
  {
296,663✔
165
    if (d_dolog && d_residx == d_resnum) {
296,663!
166
      SLOG(g_log << Logger::Warning << "Query " << ((long)(void*)this) << ": " << d_dtime.udiff() << " us total to last row" << endl,
×
167
           d_slog->info(Logr::Warning, "all query results procesed", "microseconds", Logging::Loggable(d_dtime.udiff())));
×
168
    }
×
169

170
    return d_residx < d_resnum;
296,663✔
171
  }
296,663✔
172

173
  SSqlStatement* nextRow(row_t& row) override
174
  {
272,262✔
175
    int i;
272,262✔
176
    row.clear();
272,262✔
177
    if (d_residx >= d_resnum || !d_res)
272,262!
178
      return this;
×
179
    row.reserve(PQnfields(d_res));
272,262✔
180
    for (i = 0; i < PQnfields(d_res); i++) {
2,615,958✔
181
      if (PQgetisnull(d_res, d_residx, i)) {
2,343,696✔
182
        row.emplace_back("");
198,983✔
183
      }
198,983✔
184
      else if (PQftype(d_res, i) == 16) { // BOOLEAN
2,144,713✔
185
        char* val = PQgetvalue(d_res, d_residx, i);
166✔
186
        row.emplace_back(val[0] == 't' ? "1" : "0");
166✔
187
      }
166✔
188
      else {
2,144,547✔
189
        row.emplace_back(PQgetvalue(d_res, d_residx, i));
2,144,547✔
190
      }
2,144,547✔
191
    }
2,343,696✔
192
    d_residx++;
272,262✔
193
    if (d_residx >= d_resnum) {
272,262✔
194
      PQclear(d_res);
18,526✔
195
      d_res = nullptr;
18,526✔
196
      nextResult();
18,526✔
197
    }
18,526✔
198
    return this;
272,262✔
199
  }
272,262✔
200

201
  SSqlStatement* getResult(result_t& result) override
202
  {
1,282✔
203
    result.clear();
1,282✔
204
    if (d_res == nullptr)
1,282✔
205
      return this;
178✔
206
    result.reserve(d_resnum);
1,104✔
207
    row_t row;
1,104✔
208
    while (hasNextRow()) {
2,273✔
209
      nextRow(row);
1,169✔
210
      result.push_back(std::move(row));
1,169✔
211
    }
1,169✔
212
    return this;
1,104✔
213
  }
1,282✔
214

215
  SSqlStatement* reset() override
216
  {
455,881✔
217
    int i;
455,881✔
218
    if (d_res) {
455,881!
219
      PQclear(d_res);
×
220
    }
×
221
    if (d_res_set) {
455,881!
222
      PQclear(d_res_set);
×
223
    }
×
224
    d_res_set = nullptr;
455,881✔
225
    d_res = nullptr;
455,881✔
226
    d_paridx = d_residx = d_resnum = 0;
455,881✔
227
    if (paramValues) {
455,881✔
228
      for (i = 0; i < d_nparams; i++) {
2,724,509✔
229
        if (paramValues[i]) {
2,392,207✔
230
          delete[] paramValues[i];
2,229,217✔
231
        }
2,229,217✔
232
      }
2,392,207✔
233
    }
332,302✔
234
    delete[] paramValues;
455,881✔
235
    paramValues = nullptr;
455,881✔
236
    delete[] paramLengths;
455,881✔
237
    paramLengths = nullptr;
455,881✔
238
    return this;
455,881✔
239
  }
455,881✔
240

241
  const std::string& getQuery() override { return d_query; }
×
242

243
  ~SPgSQLStatement() override
244
  {
124,053✔
245
    releaseStatement();
124,053✔
246
  }
124,053✔
247

248
private:
249
  PGconn* d_db()
250
  {
346,935✔
251
    return d_parent->db();
346,935✔
252
  }
346,935✔
253

254
  void releaseStatement()
255
  {
123,905✔
256
    d_prepared = false;
123,905✔
257
    reset();
123,905✔
258
    if (!d_stmt.empty()) {
123,905✔
259
      string cmd = string("DEALLOCATE " + d_stmt);
7,102✔
260
      PGresult* res = PQexec(d_db(), cmd.c_str());
7,102✔
261
      PQclear(res);
7,102✔
262
      d_stmt.clear();
7,102✔
263
    }
7,102✔
264
  }
123,905✔
265

266
  void prepareStatement()
267
  {
2,724,514✔
268
    if (d_prepared)
2,724,514✔
269
      return;
2,717,009✔
270
    if (d_parent->usePrepared()) {
7,507✔
271
      // prepare a statement; name must be unique per session (using d_nstatement to ensure this).
272
      this->d_stmt = string("stmt") + std::to_string(d_nstatement);
7,507✔
273
      PGresult* res = PQprepare(d_db(), d_stmt.c_str(), d_query.c_str(), d_nparams, nullptr);
7,507✔
274
      ExecStatusType status = PQresultStatus(res);
7,507✔
275
      string errmsg(PQresultErrorMessage(res));
7,507✔
276
      PQclear(res);
7,507✔
277
      if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_NONFATAL_ERROR) {
7,507!
278
        releaseStatement();
×
279
        throw SSqlException("Fatal error during prePQpreparepare: " + d_query + string(": ") + errmsg);
×
280
      }
×
281
    }
7,507✔
282
    paramValues = nullptr;
7,505✔
283
    paramLengths = nullptr;
7,505✔
284
    d_cur_set = d_paridx = d_residx = d_resnum = 0;
7,505✔
285
    d_res = nullptr;
7,505✔
286
    d_res_set = nullptr;
7,505✔
287
    d_prepared = true;
7,505✔
288
  }
7,505✔
289

290
  void allocate()
291
  {
2,229,212✔
292
    if (paramValues != nullptr)
2,229,212✔
293
      return;
1,896,915✔
294
    paramValues = new char*[d_nparams];
332,297✔
295
    paramLengths = new int[d_nparams];
332,297✔
296
    memset(paramValues, 0, sizeof(char*) * d_nparams);
332,297✔
297
    memset(paramLengths, 0, sizeof(int) * d_nparams);
332,297✔
298
  }
332,297✔
299

300
  string d_query;
301
  string d_stmt;
302
  SPgSQL* d_parent;
303
  PGresult* d_res_set{nullptr};
304
  PGresult* d_res{nullptr};
305
  bool d_dolog;
306
  std::shared_ptr<Logr::Logger> d_slog;
307
  DTime d_dtime; // only used if d_dolog is set
308
  bool d_prepared{false};
309
  int d_nparams;
310
  int d_paridx{0};
311
  char** paramValues{nullptr};
312
  int* paramLengths{nullptr};
313
  int d_residx{0};
314
  int d_resnum{0};
315
  int d_cur_set{0};
316
  unsigned int d_nstatement;
317
};
318

319
bool SPgSQL::s_dolog;
320

321
static string escapeForPQparam(const string& v)
322
{
3,345✔
323
  string ret = v;
3,345✔
324
  boost::replace_all(ret, "\\", "\\\\");
3,345✔
325
  boost::replace_all(ret, "'", "\\'");
3,345✔
326

327
  return string("'") + ret + string("'");
3,345✔
328
}
3,345✔
329

330
SPgSQL::SPgSQL(Logr::log_t log, const string& database, const string& host, const string& port, const string& user,
331
               const string& password, const string& extra_connection_parameters, const bool use_prepared)
332
{
1,931✔
333
  d_slog = log;
1,931✔
334
  d_db = nullptr;
1,931✔
335
  d_in_trx = false;
1,931✔
336
  d_connectstr = "";
1,931✔
337
  d_nstatements = 0;
1,931✔
338

339
  if (!database.empty())
1,931!
340
    d_connectstr += "dbname=" + escapeForPQparam(database);
1,931✔
341

342
  if (!user.empty())
1,931✔
343
    d_connectstr += " user=" + escapeForPQparam(user);
1,414✔
344

345
  if (!host.empty())
1,931!
346
    d_connectstr += " host=" + escapeForPQparam(host);
×
347

348
  if (!port.empty())
1,931!
349
    d_connectstr += " port=" + escapeForPQparam(port);
×
350

351
  if (!extra_connection_parameters.empty())
1,931!
352
    d_connectstr += " " + extra_connection_parameters;
×
353

354
  d_connectlogstr = d_connectstr;
1,931✔
355

356
  if (!password.empty()) {
1,931!
357
    d_connectlogstr += " password=<HIDDEN>";
×
358
    d_connectstr += " password=" + escapeForPQparam(password);
×
359
  }
×
360

361
  d_use_prepared = use_prepared;
1,931✔
362

363
  d_db = PQconnectdb(d_connectstr.c_str());
1,931✔
364

365
  if (!d_db || PQstatus(d_db) == CONNECTION_BAD) {
1,931!
366
    try {
×
367
      throw sPerrorException("Unable to connect to database, connect string: " + d_connectlogstr);
×
368
    }
×
369
    catch (...) {
×
370
      if (d_db)
×
371
        PQfinish(d_db);
×
372
      d_db = 0;
×
373
      throw;
×
374
    }
×
375
  }
×
376
}
1,931✔
377

378
void SPgSQL::setLog(bool state)
379
{
1,931✔
380
  s_dolog = state;
1,931✔
381
}
1,931✔
382

383
SPgSQL::~SPgSQL()
384
{
1,827✔
385
  PQfinish(d_db);
1,827✔
386
}
1,827✔
387

388
SSqlException SPgSQL::sPerrorException(const string& reason)
389
{
×
390
  return SSqlException(reason + string(": ") + (d_db ? PQerrorMessage(d_db) : "no connection"));
×
391
}
×
392

393
void SPgSQL::execute(const string& query)
394
{
1,151✔
395
  PGresult* res = PQexec(d_db, query.c_str());
1,151✔
396
  ExecStatusType status = PQresultStatus(res);
1,151✔
397
  string errmsg(PQresultErrorMessage(res));
1,151✔
398
  PQclear(res);
1,151✔
399
  if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_NONFATAL_ERROR) {
1,151!
400
    throw sPerrorException("Fatal error during query: " + errmsg);
×
401
  }
×
402
}
1,151✔
403

404
std::unique_ptr<SSqlStatement> SPgSQL::prepare(const string& query, int nparams)
405
{
131,305✔
406
  d_nstatements++;
131,305✔
407
  return std::make_unique<SPgSQLStatement>(d_slog, query, s_dolog, nparams, this, d_nstatements);
131,305✔
408
}
131,305✔
409

410
void SPgSQL::startTransaction()
411
{
583✔
412
  execute("begin");
583✔
413
  d_in_trx = true;
583✔
414
}
583✔
415

416
void SPgSQL::commit()
417
{
542✔
418
  execute("commit");
542✔
419
  d_in_trx = false;
542✔
420
}
542✔
421

422
void SPgSQL::rollback()
423
{
26✔
424
  execute("rollback");
26✔
425
  d_in_trx = false;
26✔
426
}
26✔
427

428
bool SPgSQL::isConnectionUsable()
429
{
23,480✔
430
  if (PQstatus(d_db) != CONNECTION_OK) {
23,480!
431
    return false;
×
432
  }
×
433

434
  bool usable = false;
23,480✔
435
  int sd = PQsocket(d_db);
23,480✔
436
  bool wasNonBlocking = isNonBlocking(sd);
23,480✔
437

438
  if (!wasNonBlocking) {
23,480!
439
    if (!setNonBlocking(sd)) {
×
440
      return usable;
×
441
    }
×
442
  }
×
443

444
  usable = isTCPSocketUsable(sd);
23,480✔
445

446
  if (!wasNonBlocking) {
23,480!
447
    if (!setBlocking(sd)) {
×
448
      usable = false;
×
449
    }
×
450
  }
×
451

452
  return usable;
23,480✔
453
}
23,480✔
454

455
void SPgSQL::reconnect()
456
{
×
457
  PQreset(d_db);
×
458
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc