From: Chris Hofstaedtler Date: Tue, 2 Jun 2020 18:40:29 +0000 (+0200) Subject: gpgsql: Reintroduce prepared statements X-Git-Tag: dnsdist-1.5.0-rc3~12^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4a5993df5ee8ecc6ba007150da80a81373bad51b;p=thirdparty%2Fpdns.git gpgsql: Reintroduce prepared statements And a toggle. --- diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 2e2383e435..b163aa7d63 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1468,6 +1468,7 @@ policykinds policyname pollmplexer Ponomarev +poolers poolname portnum portnumber diff --git a/docs/backends/generic-postgresql.rst b/docs/backends/generic-postgresql.rst index f65b4bf970..f746283df5 100644 --- a/docs/backends/generic-postgresql.rst +++ b/docs/backends/generic-postgresql.rst @@ -91,6 +91,17 @@ parameters are documented `in the PostgreSQL documentation `__. 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 -------------- diff --git a/modules/gpgsqlbackend/gpgsqlbackend.cc b/modules/gpgsqlbackend/gpgsqlbackend.cc index c8e38a8607..53833ee65a 100644 --- a/modules/gpgsqlbackend/gpgsqlbackend.cc +++ b/modules/gpgsqlbackend/gpgsqlbackend.cc @@ -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"); diff --git a/modules/gpgsqlbackend/spgsql.cc b/modules/gpgsqlbackend/spgsql.cc index 68e5be885f..870469c5ad 100644 --- a/modules/gpgsqlbackend/spgsql.cc +++ b/modules/gpgsqlbackend/spgsql.cc @@ -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<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 SPgSQL::prepare(const string& query, int nparams) { - return std::unique_ptr(new SPgSQLStatement(query, s_dolog, nparams, this)); + d_nstatements++; + return std::unique_ptr(new SPgSQLStatement(query, s_dolog, nparams, this, d_nstatements)); } void SPgSQL::startTransaction() { diff --git a/modules/gpgsqlbackend/spgsql.hh b/modules/gpgsqlbackend/spgsql.hh index c0aab35ca1..aea85c4221 100644 --- a/modules/gpgsqlbackend/spgsql.hh +++ b/modules/gpgsqlbackend/spgsql.hh @@ -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; };