2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
30 #include "pdns/logger.hh"
31 #include "pdns/dns.hh"
32 #include "pdns/namespaces.hh"
35 class SPgSQLStatement
: public SSqlStatement
38 SPgSQLStatement(const string
& query
, bool dolog
, int nparams
, SPgSQL
* db
, unsigned int nstatement
) {
48 d_nstatement
= nstatement
;
56 SSqlStatement
* bind(const string
& name
, bool value
) { return bind(name
, string(value
? "t" : "f")); }
57 SSqlStatement
* bind(const string
& name
, int value
) { return bind(name
, std::to_string(value
)); }
58 SSqlStatement
* bind(const string
& name
, uint32_t value
) { return bind(name
, std::to_string(value
)); }
59 SSqlStatement
* bind(const string
& name
, long value
) { return bind(name
, std::to_string(value
)); }
60 SSqlStatement
* bind(const string
& name
, unsigned long value
) { return bind(name
, std::to_string(value
)); }
61 SSqlStatement
* bind(const string
& name
, long long value
) { return bind(name
, std::to_string(value
)); }
62 SSqlStatement
* bind(const string
& name
, unsigned long long value
) { return bind(name
, std::to_string(value
)); }
63 SSqlStatement
* bind(const string
& name
, const std::string
& value
) {
66 if (d_paridx
>=d_nparams
) {
68 throw SSqlException("Attempt to bind more parameters than query has: " + d_query
);
70 paramValues
[d_paridx
] = new char[value
.size()+1];
71 memset(paramValues
[d_paridx
], 0, sizeof(char)*(value
.size()+1));
72 value
.copy(paramValues
[d_paridx
], value
.size());
73 paramLengths
[d_paridx
] = value
.size();
77 SSqlStatement
* bindNull(const string
& name
) { prepareStatement(); d_paridx
++; return this; } // these are set null in allocate()
78 SSqlStatement
* execute() {
81 g_log
<<Logger::Warning
<<"Query: "<<d_query
<<endl
;
83 d_res_set
= PQexecPrepared(d_db(), d_stmt
.c_str(), d_nparams
, paramValues
, paramLengths
, NULL
, 0);
84 ExecStatusType status
= PQresultStatus(d_res_set
);
85 if (status
!= PGRES_COMMAND_OK
&& status
!= PGRES_TUPLES_OK
&& status
!= PGRES_NONFATAL_ERROR
) {
86 string
errmsg(PQresultErrorMessage(d_res_set
));
88 throw SSqlException("Fatal error during query: " + d_query
+ string(": ") + errmsg
);
96 if (d_res_set
== NULL
) return; // no refcursor
97 if (d_cur_set
>= PQntuples(d_res_set
)) {
102 // this code handles refcursors if they are returned
103 // by stored procedures. you can return more than one
104 // if you return SETOF refcursor.
105 if (PQftype(d_res_set
, 0) == 1790) { // REFCURSOR
106 #if PG_VERSION_NUM > 90000
107 // PQescapeIdentifier was added to libpq in postgresql 9.0
108 char *val
= PQgetvalue(d_res_set
, d_cur_set
++, 0);
109 char *portal
= PQescapeIdentifier(d_db(), val
, strlen(val
));
110 string cmd
= string("FETCH ALL FROM \"") + string(portal
) + string("\"");
113 string portal
= string(PQgetvalue(d_res_set
, d_cur_set
++, 0));
114 string cmd
= string("FETCH ALL FROM \"") + portal
+ string("\"");
118 g_log
<<Logger::Warning
<<"Query: "<<cmd
<<endl
;
119 d_res
= PQexec(d_db(),cmd
.c_str());
120 d_resnum
= PQntuples(d_res
);
121 d_fnum
= PQnfields(d_res
);
126 d_resnum
= PQntuples(d_res
);
127 d_fnum
= PQnfields(d_res
);
133 return d_residx
<d_resnum
;
136 SSqlStatement
* nextRow(row_t
& row
) {
139 if (d_residx
>=d_resnum
|| !d_res
) return this;
140 row
.reserve(PQnfields(d_res
));
141 for(i
=0;i
<PQnfields(d_res
);i
++) {
142 if (PQgetisnull(d_res
, d_residx
, i
)) {
144 } else if (PQftype(d_res
, i
) == 16) { // BOOLEAN
145 char *val
= PQgetvalue(d_res
, d_residx
, i
);
146 row
.push_back(val
[0] == 't' ? "1" : "0");
148 row
.push_back(string(PQgetvalue(d_res
, d_residx
, i
)));
152 if (d_residx
>= d_resnum
) {
160 SSqlStatement
* getResult(result_t
& result
) {
162 if (d_res
== NULL
) return this;
163 result
.reserve(d_resnum
);
165 while(hasNextRow()) { nextRow(row
); result
.push_back(row
); }
169 SSqlStatement
* reset() {
177 d_paridx
= d_residx
= d_resnum
= 0;
179 for(i
=0;i
<d_nparams
;i
++)
180 if (paramValues
[i
]) delete [] paramValues
[i
];
181 delete [] paramValues
;
183 delete [] paramLengths
;
188 const std::string
& getQuery() { return d_query
; }
195 return d_parent
->db();
198 void releaseStatement() {
201 if (!d_stmt
.empty()) {
202 string cmd
= string("DEALLOCATE " + d_stmt
);
203 PGresult
*res
= PQexec(d_db(), cmd
.c_str());
209 void prepareStatement() {
210 if (d_prepared
) return;
211 // prepare a statement; name must be unique per session (using d_nstatement to ensure this).
212 this->d_stmt
= string("stmt") + std::to_string(d_nstatement
);
213 PGresult
* res
= PQprepare(d_db(), d_stmt
.c_str(), d_query
.c_str(), d_nparams
, NULL
);
214 ExecStatusType status
= PQresultStatus(res
);
215 string
errmsg(PQresultErrorMessage(res
));
217 if (status
!= PGRES_COMMAND_OK
&& status
!= PGRES_TUPLES_OK
&& status
!= PGRES_NONFATAL_ERROR
) {
219 throw SSqlException("Fatal error during prepare: " + d_query
+ string(": ") + errmsg
);
222 d_cur_set
=d_paridx
=d_residx
=d_resnum
=d_fnum
=0;
230 if (paramValues
!= NULL
) return;
231 paramValues
= new char*[d_nparams
];
232 paramLengths
= new int[d_nparams
];
233 memset(paramValues
, 0, sizeof(char*)*d_nparams
);
234 memset(paramLengths
, 0, sizeof(int)*d_nparams
);
252 unsigned int d_nstatement
;
255 bool SPgSQL::s_dolog
;
257 SPgSQL::SPgSQL(const string
&database
, const string
&host
, const string
& port
, const string
&user
,
258 const string
&password
, const string
&extra_connection_parameters
)
265 if (!database
.empty())
266 d_connectstr
+="dbname="+database
;
269 d_connectstr
+=" user="+user
;
272 d_connectstr
+=" host="+host
;
275 d_connectstr
+=" port="+port
;
277 if(!extra_connection_parameters
.empty())
278 d_connectstr
+=" " + extra_connection_parameters
;
280 d_connectlogstr
=d_connectstr
;
282 if(!password
.empty()) {
283 d_connectlogstr
+=" password=<HIDDEN>";
284 d_connectstr
+=" password="+password
;
287 d_db
=PQconnectdb(d_connectstr
.c_str());
289 if (!d_db
|| PQstatus(d_db
)==CONNECTION_BAD
) {
291 throw sPerrorException("Unable to connect to database, connect string: "+d_connectlogstr
);
302 void SPgSQL::setLog(bool state
)
312 SSqlException
SPgSQL::sPerrorException(const string
&reason
)
314 return SSqlException(reason
+string(": ")+(d_db
? PQerrorMessage(d_db
) : "no connection"));
317 void SPgSQL::execute(const string
& query
)
319 PGresult
* res
= PQexec(d_db
, query
.c_str());
320 ExecStatusType status
= PQresultStatus(res
);
321 string
errmsg(PQresultErrorMessage(res
));
323 if (status
!= PGRES_COMMAND_OK
&& status
!= PGRES_TUPLES_OK
&& status
!= PGRES_NONFATAL_ERROR
) {
324 throw sPerrorException("Fatal error during query: " + errmsg
);
328 std::unique_ptr
<SSqlStatement
> SPgSQL::prepare(const string
& query
, int nparams
)
331 return std::unique_ptr
<SSqlStatement
>(new SPgSQLStatement(query
, s_dolog
, nparams
, this, d_nstatement
));
334 void SPgSQL::startTransaction() {
339 void SPgSQL::commit() {
344 void SPgSQL::rollback() {
349 bool SPgSQL::isConnectionUsable()
351 if (PQstatus(d_db
) != CONNECTION_OK
) {
356 int sd
= PQsocket(d_db
);
357 bool wasNonBlocking
= isNonBlocking(sd
);
359 if (!wasNonBlocking
) {
360 if (!setNonBlocking(sd
)) {
365 usable
= isTCPSocketUsable(sd
);
367 if (!wasNonBlocking
) {
368 if (!setBlocking(sd
)) {
376 void SPgSQL::reconnect()