]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
gpgsql: Reintroduce prepared statements
authorChris Hofstaedtler <chris.hofstaedtler@deduktiva.com>
Tue, 2 Jun 2020 18:40:29 +0000 (20:40 +0200)
committerChris Hofstaedtler <chris.hofstaedtler@deduktiva.com>
Tue, 2 Jun 2020 18:52:44 +0000 (20:52 +0200)
And a toggle.

.github/actions/spell-check/expect.txt
docs/backends/generic-postgresql.rst
modules/gpgsqlbackend/gpgsqlbackend.cc
modules/gpgsqlbackend/spgsql.cc
modules/gpgsqlbackend/spgsql.hh

index 2e2383e435863c0b68db7362b3386a13cb91ad71..b163aa7d6394765869fa0e406137e48970af7218 100644 (file)
@@ -1468,6 +1468,7 @@ policykinds
 policyname
 pollmplexer
 Ponomarev
+poolers
 poolname
 portnum
 portnumber
index f65b4bf970316465e1f73423e7b3803d9233b992..f746283df5abd36f9bc437c899e38eab8bfaa343 100644 (file)
@@ -91,6 +91,17 @@ parameters are documented `in the PostgreSQL
 documentation <https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS>`__.
 Default: "".
 
+.. _setting-gpgsql-prepared-statements:
+
+``gpgsql-prepared-statements``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Prepare statements for better performance, instead of sending parameterized queries.
+Might not work with connection poolers.
+Default: yes.
+
+.. versionadded:: 4.4.0
+
 Default schema
 --------------
 
index c8e38a86073717a4d7a7d8c6a4dd5cfa241727c4..53833ee65a2a6e2b505bca118b2beff0377d530d 100644 (file)
@@ -40,11 +40,12 @@ gPgSQLBackend::gPgSQLBackend(const string &mode, const string &suffix)  : GSQLBa
 {
   try {
     setDB(new SPgSQL(getArg("dbname"),
-                 getArg("host"),
-                 getArg("port"),
-                 getArg("user"),
-                 getArg("password"),
-                 getArg("extra-connection-parameters")));
+                     getArg("host"),
+                     getArg("port"),
+                     getArg("user"),
+                     getArg("password"),
+                     getArg("extra-connection-parameters"),
+                     mustDo("prepared-statements")));
   }
 
   catch(SSqlException &e) {
@@ -87,6 +88,7 @@ public:
     declare(suffix,"port","Database backend port to connect to","");
     declare(suffix,"password","Database backend password to connect with","");
     declare(suffix,"extra-connection-parameters", "Extra parameters to add to connection string","");
+    declare(suffix,"prepared-statements", "Use prepared statements instead of parameterized queries", "yes");
 
     declare(suffix,"dnssec","Enable DNSSEC processing","no");
 
index 68e5be885fc7cf88d576a52ed39185d3481c27c0..870469c5ad3cbf3e03860fd1a78bb29fbf0776e5 100644 (file)
@@ -35,7 +35,7 @@
 class SPgSQLStatement: public SSqlStatement
 {
 public:
-  SPgSQLStatement(const string& query, bool dolog, int nparams, SPgSQL* db) {
+  SPgSQLStatement(const string& query, bool dolog, int nparams, SPgSQL* db, unsigned int nstatement) {
     d_query = query;
     d_dolog = dolog;
     d_parent = db;
@@ -45,6 +45,7 @@ public:
     d_res_set = NULL;
     paramValues = NULL;
     paramLengths = NULL;
+    d_nstatement = nstatement;
     d_paridx = 0;
     d_residx = 0;
     d_resnum = 0;
@@ -80,7 +81,11 @@ public:
       g_log<<Logger::Warning<< "Query "<<((long)(void*)this)<<": " << d_query << endl;
       d_dtime.set();
     }
-    d_res_set = PQexecParams(d_db(), d_query.c_str(), d_nparams, NULL, paramValues, paramLengths, NULL, 0);
+    if (!d_stmt.empty()) {
+      d_res_set = PQexecPrepared(d_db(), d_stmt.c_str(), d_nparams, paramValues, paramLengths, NULL, 0);
+    } else {
+      d_res_set = PQexecParams(d_db(), d_query.c_str(), d_nparams, NULL, paramValues, paramLengths, NULL, 0);
+    }
     ExecStatusType status = PQresultStatus(d_res_set);
     if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_NONFATAL_ERROR) {
       string errmsg(PQresultErrorMessage(d_res_set));
@@ -207,10 +212,28 @@ private:
   void releaseStatement() {
     d_prepared = false;
     reset();
+    if (!d_stmt.empty()) {
+      string cmd = string("DEALLOCATE " + d_stmt);
+      PGresult *res = PQexec(d_db(), cmd.c_str());
+      PQclear(res);
+      d_stmt.clear();
+    }
   }
 
   void prepareStatement() {
     if (d_prepared) return;
+    if (d_parent->usePrepared()) {
+      // prepare a statement; name must be unique per session (using d_nstatement to ensure this).
+      this->d_stmt = string("stmt") + std::to_string(d_nstatement);
+      PGresult* res = PQprepare(d_db(), d_stmt.c_str(), d_query.c_str(), d_nparams, NULL);
+      ExecStatusType status = PQresultStatus(res);
+      string errmsg(PQresultErrorMessage(res));
+      PQclear(res);
+      if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_NONFATAL_ERROR) {
+        releaseStatement();
+        throw SSqlException("Fatal error during prePQpreparepare: " + d_query + string(": ") + errmsg);
+      }
+    }
     paramValues=NULL;
     d_cur_set=d_paridx=d_residx=d_resnum=d_fnum=0;
     paramLengths=NULL;
@@ -228,6 +251,7 @@ private:
   }
 
   string d_query;
+  string d_stmt;
   SPgSQL *d_parent;
   PGresult *d_res_set;
   PGresult *d_res;
@@ -242,16 +266,18 @@ private:
   int d_resnum;
   int d_fnum;
   int d_cur_set;
+  unsigned int d_nstatement;
 };
 
 bool SPgSQL::s_dolog;
 
 SPgSQL::SPgSQL(const string &database, const string &host, const string& port, const string &user,
-               const string &password, const string &extra_connection_parameters)
+               const string &password, const string &extra_connection_parameters, const bool use_prepared)
 {
-  d_db=0;
+  d_db = nullptr;
   d_in_trx = false;
-  d_connectstr="";
+  d_connectstr = "";
+  d_nstatements = 0;
 
   if (!database.empty())
     d_connectstr+="dbname="+database;
@@ -275,6 +301,8 @@ SPgSQL::SPgSQL(const string &database, const string &host, const string& port, c
     d_connectstr+=" password="+password;
   }
 
+  d_use_prepared = use_prepared;
+
   d_db=PQconnectdb(d_connectstr.c_str());
 
   if (!d_db || PQstatus(d_db)==CONNECTION_BAD) {
@@ -318,7 +346,8 @@ void SPgSQL::execute(const string& query)
 
 std::unique_ptr<SSqlStatement> SPgSQL::prepare(const string& query, int nparams)
 {
-  return std::unique_ptr<SSqlStatement>(new SPgSQLStatement(query, s_dolog, nparams, this));
+  d_nstatements++;
+  return std::unique_ptr<SSqlStatement>(new SPgSQLStatement(query, s_dolog, nparams, this, d_nstatements));
 }
 
 void SPgSQL::startTransaction() {
index c0aab35ca1625c5a59382b9bf4bc99018676c910..aea85c422197457210a664eb18e0b6508eebc02a 100644 (file)
@@ -29,7 +29,7 @@ class SPgSQL : public SSql
 public:
   SPgSQL(const string &database, const string &host="", const string& port="",
          const string &user="", const string &password="",
-         const string &extra_connection_parameters="");
+         const string &extra_connection_parameters="", const bool use_prepared = true);
 
   ~SPgSQL();
   
@@ -47,6 +47,7 @@ public:
 
   PGconn* db() { return d_db; }
   bool in_trx() const { return d_in_trx; }
+  bool usePrepared() { return d_use_prepared; }
 
 private:
   PGconn* d_db;
@@ -54,4 +55,6 @@ private:
   string d_connectlogstr;
   static bool s_dolog;
   bool d_in_trx;
+  bool d_use_prepared;
+  unsigned int d_nstatements;
 };