]> granicus.if.org Git - pdns/commitdiff
backport #6134: auth: Always bind the results array after executing a statement
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 2 Jan 2018 16:03:47 +0000 (17:03 +0100)
committerPeter van Dijk <peter.van.dijk@powerdns.com>
Tue, 13 Feb 2018 11:31:15 +0000 (12:31 +0100)
We will reuse the same array most of the time, but it turns out that
calling mysql_stmt_next_result() followed by mysql_stmt_store_result()
invalidates the existing binding (the first one sets stmt->bind_result_done
to false, causing the second to reset the existing binding).

(cherry picked from commit 4fd90e75d47d6ec43d10c94ea260b08e50806442)

modules/gmysqlbackend/smysql.cc

index aab16daf9d5643b381501cb3bc0f728c2407fbf5..8a2a5092a3fc9c41c43dbc350289a477365cdb09 100644 (file)
@@ -182,7 +182,7 @@ public:
       // prepare for result
       d_resnum = mysql_stmt_num_rows(d_stmt);
       
-      if (d_resnum>0 && d_res_bind == NULL) {
+      if (d_resnum > 0 && d_res_bind == nullptr) {
         MYSQL_RES* meta = mysql_stmt_result_metadata(d_stmt);
         d_fnum = static_cast<int>(mysql_num_fields(meta)); // ensure correct number of fields
         d_res_bind = new MYSQL_BIND[d_fnum];
@@ -201,12 +201,17 @@ public:
         }
   
         mysql_free_result(meta);
-  
-        if ((err = mysql_stmt_bind_result(d_stmt, d_res_bind))) {
-          string error(mysql_stmt_error(d_stmt));
-          releaseStatement();
-          throw SSqlException("Could not bind parameters to mysql statement: " + d_query + string(": ") + error);
-        }
+      }
+
+      /* we need to bind the results array again because a call to mysql_stmt_next_result() followed
+         by a call to mysql_stmt_store_result() might have invalidated it (the first one sets
+         stmt->bind_result_done to false, causing the second to reset the existing binding),
+         and we can't bind it right after the call to mysql_stmt_store_result() if it returned
+         no rows, because then the statement 'contains no metadata' */
+      if (d_res_bind != nullptr && (err = mysql_stmt_bind_result(d_stmt, d_res_bind))) {
+        string error(mysql_stmt_error(d_stmt));
+        releaseStatement();
+        throw SSqlException("Could not bind parameters to mysql statement: " + d_query + string(": ") + error);
       }
     }
 
@@ -220,9 +225,11 @@ public:
   SSqlStatement* nextRow(row_t& row) {
     int err;
     row.clear();
-    if (!hasNextRow()) return this;
+    if (!hasNextRow()) {
+      return this;
+    }
 
-    if ((err =mysql_stmt_fetch(d_stmt))) {
+    if ((err = mysql_stmt_fetch(d_stmt))) {
       if (err != MYSQL_DATA_TRUNCATED) {
         string error(mysql_stmt_error(d_stmt));
         releaseStatement();
@@ -252,13 +259,13 @@ public:
         if ((err = mysql_stmt_store_result(d_stmt))) {
           string error(mysql_stmt_error(d_stmt));
           releaseStatement();
-          throw SSqlException("Could not store mysql statement: " + d_query + string(": ") + error);
+          throw SSqlException("Could not store mysql statement while processing additional sets: " + d_query + string(": ") + error);
         }
         d_resnum = mysql_stmt_num_rows(d_stmt);
         // XXX: For some reason mysql_stmt_result_metadata returns NULL here, so we cannot
         // ensure row field count matches first result set.
-        if (d_resnum>0) { // ignore empty result set
-          if ((err = mysql_stmt_bind_result(d_stmt, d_res_bind))) {
+        if (d_resnum > 0) { // ignore empty result set
+          if (d_res_bind != nullptr && (err = mysql_stmt_bind_result(d_stmt, d_res_bind))) {
             string error(mysql_stmt_error(d_stmt));
             releaseStatement();
             throw SSqlException("Could not bind parameters to mysql statement: " + d_query + string(": ") + error);