avoid failing hard if 'gnunetcheck' db does not exist
[oweals/gnunet.git] / src / pq / pq_eval.c
index 39a7a2c9894513b2471ea531873a5b64e2b1ebe0..2beb3475b5e74153414832ec5b5c9617a5441a3e 100644 (file)
@@ -2,16 +2,20 @@
   This file is part of GNUnet
   Copyright (C) 2017 GNUnet e.V.
 
-  GNUnet is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
+  GNUnet is free software: you can redistribute it and/or modify it
+  under the terms of the GNU Affero General Public License as published
+  by the Free Software Foundation, either version 3 of the License,
+  or (at your option) any later version.
 
-  GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Affero General Public License for more details.
 
-  You should have received a copy of the GNU General Public License along with
-  GNUnet; see the file COPYING.  If not, If not, see <http://www.gnu.org/licenses/>
+  You should have received a copy of the GNU Affero General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
 */
 /**
  * @file pq/pq_eval.c
  * @param statement_name name of the statement that created @a result
  * @param result result to check
  * @return status code from the result, mapping PQ status
- *         codes to `enum GNUNET_PQ_QueryStatus`.  Never
+ *         codes to `enum GNUNET_DB_QueryStatus`.  Never
  *         returns positive values as this function does
  *         not look at the result set.
  * @deprecated (low level, let's see if we can do with just the high-level functions)
  */
-enum GNUNET_PQ_QueryStatus
+enum GNUNET_DB_QueryStatus
 GNUNET_PQ_eval_result (PGconn *connection,
                        const char *statement_name,
                        PGresult *result)
 {
-  if (PGRES_COMMAND_OK !=
-      PQresultStatus (result))
+  ExecStatusType est;
+
+  est = PQresultStatus (result);
+  if ( (PGRES_COMMAND_OK != est) &&
+       (PGRES_TUPLES_OK != est) )
   {
     const char *sqlstate;
 
@@ -68,7 +75,7 @@ GNUNET_PQ_eval_result (PGconn *connection,
     {
       /* very unexpected... */
       GNUNET_break (0);
-      return GNUNET_PQ_STATUS_HARD_ERROR;
+      return GNUNET_DB_STATUS_HARD_ERROR;
     }
     if ( (0 == strcmp (sqlstate,
                        PQ_DIAG_SQLSTATE_DEADLOCK)) ||
@@ -88,7 +95,24 @@ GNUNET_PQ_eval_result (PGconn *connection,
                        PQresultErrorMessage (result),
                        PQresStatus (PQresultStatus (result)),
                        PQerrorMessage (connection));
-      return GNUNET_PQ_STATUS_SOFT_ERROR;
+      return GNUNET_DB_STATUS_SOFT_ERROR;
+    }
+    if (0 == strcmp (sqlstate,
+                     PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION))
+    {
+      /* Likely no need to retry, INSERT of "same" data. */
+      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
+                       "pq",
+                       "Query `%s' failed with unique violation: %s/%s/%s/%s/%s\n",
+                       statement_name,
+                       PQresultErrorField (result,
+                                           PG_DIAG_MESSAGE_PRIMARY),
+                       PQresultErrorField (result,
+                                           PG_DIAG_MESSAGE_DETAIL),
+                       PQresultErrorMessage (result),
+                       PQresStatus (PQresultStatus (result)),
+                       PQerrorMessage (connection));
+      return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     }
     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
                      "pq",
@@ -101,9 +125,9 @@ GNUNET_PQ_eval_result (PGconn *connection,
                      PQresultErrorMessage (result),
                      PQresStatus (PQresultStatus (result)),
                      PQerrorMessage (connection));
-    return GNUNET_PQ_STATUS_HARD_ERROR;
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
-  return GNUNET_PQ_STATUS_SUCCESS_NO_RESULTS;
+  return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
 }
 
 
@@ -116,17 +140,20 @@ GNUNET_PQ_eval_result (PGconn *connection,
  * @param statement_name name of the statement
  * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
  * @return status code from the result, mapping PQ status
- *         codes to `enum GNUNET_PQ_QueryStatus`.  Never
- *         returns positive values as this function does
- *         not look at the result set.
+ *         codes to `enum GNUNET_DB_QueryStatus`.  If the
+ *         statement was a DELETE or UPDATE statement, the
+ *         number of affected rows is returned.; if the
+ *         statment was an INSERT statement, and no row
+ *         was added due to a UNIQUE violation, we return
+ *         zero; if INSERT was successful, we return one.
  */
-enum GNUNET_PQ_QueryStatus
+enum GNUNET_DB_QueryStatus
 GNUNET_PQ_eval_prepared_non_select (PGconn *connection,
                                     const char *statement_name,
                                     const struct GNUNET_PQ_QueryParam *params)
 {
   PGresult *result;
-  enum GNUNET_PQ_QueryStatus qs;
+  enum GNUNET_DB_QueryStatus qs;
 
   result = GNUNET_PQ_exec_prepared (connection,
                                     statement_name,
@@ -134,6 +161,15 @@ GNUNET_PQ_eval_prepared_non_select (PGconn *connection,
   qs = GNUNET_PQ_eval_result (connection,
                               statement_name,
                               result);
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+  {
+    const char *tuples;
+
+    /* What an awful API, this function really does return a string */
+    tuples = PQcmdTuples (result);
+    if (NULL != tuples)
+      qs = strtol (tuples, NULL, 10);
+  }
   PQclear (result);
   return qs;
 }
@@ -152,9 +188,9 @@ GNUNET_PQ_eval_prepared_non_select (PGconn *connection,
  * @param rh function to call with the result set, NULL to ignore
  * @param rh_cls closure to pass to @a rh
  * @return status code from the result, mapping PQ status
- *         codes to `enum GNUNET_PQ_QueryStatus`.
+ *         codes to `enum GNUNET_DB_QueryStatus`.
  */
-enum GNUNET_PQ_QueryStatus
+enum GNUNET_DB_QueryStatus
 GNUNET_PQ_eval_prepared_multi_select (PGconn *connection,
                                       const char *statement_name,
                                       const struct GNUNET_PQ_QueryParam *params,
@@ -162,7 +198,7 @@ GNUNET_PQ_eval_prepared_multi_select (PGconn *connection,
                                       void *rh_cls)
 {
   PGresult *result;
-  enum GNUNET_PQ_QueryStatus qs;
+  enum GNUNET_DB_QueryStatus qs;
   unsigned int ret;
 
   result = GNUNET_PQ_exec_prepared (connection,
@@ -191,7 +227,7 @@ GNUNET_PQ_eval_prepared_multi_select (PGconn *connection,
  * which must return a single result in @a connection using the given
  * @a params.  Stores the result (if any) in @a rs, which the caller
  * must then clean up using #GNUNET_PQ_cleanup_result() if the return
- * value was #GNUNET_PQ_STATUS_SUCCESS_ONE_RESULT.  Returns the
+ * value was #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT.  Returns the
  * resulting session status.
  *
  * @param connection connection to execute the statement in
@@ -199,16 +235,16 @@ GNUNET_PQ_eval_prepared_multi_select (PGconn *connection,
  * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
  * @param[in,out] rs result specification to use for storing the result of the query
  * @return status code from the result, mapping PQ status
- *         codes to `enum GNUNET_PQ_QueryStatus`.
+ *         codes to `enum GNUNET_DB_QueryStatus`.
  */
-enum GNUNET_PQ_QueryStatus
+enum GNUNET_DB_QueryStatus
 GNUNET_PQ_eval_prepared_singleton_select (PGconn *connection,
                                           const char *statement_name,
                                           const struct GNUNET_PQ_QueryParam *params,
                                           struct GNUNET_PQ_ResultSpec *rs)
 {
   PGresult *result;
-  enum GNUNET_PQ_QueryStatus qs;
+  enum GNUNET_DB_QueryStatus qs;
 
   result = GNUNET_PQ_exec_prepared (connection,
                                     statement_name,
@@ -224,14 +260,14 @@ GNUNET_PQ_eval_prepared_singleton_select (PGconn *connection,
   if (0 == PQntuples (result))
   {
     PQclear (result);
-    return GNUNET_PQ_STATUS_SUCCESS_NO_RESULTS;
+    return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
   }
   if (1 != PQntuples (result))
   {
     /* more than one result, but there must be at most one */
     GNUNET_break (0);
     PQclear (result);
-    return GNUNET_PQ_STATUS_HARD_ERROR;
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
   if (GNUNET_OK !=
       GNUNET_PQ_extract_result (result,
@@ -239,10 +275,10 @@ GNUNET_PQ_eval_prepared_singleton_select (PGconn *connection,
                                 0))
   {
     PQclear (result);
-    return GNUNET_PQ_STATUS_HARD_ERROR;
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
   PQclear (result);
-  return GNUNET_PQ_STATUS_SUCCESS_ONE_RESULT;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }