update_client.c (3940B)
1 /* 2 This file is part of Challenger 3 Copyright (C) 2024 Taler Systems SA 4 5 Challenger is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Challenger is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file src/challengerdb/update_client.c 18 * @brief Implementation of the update_client function for Postgres 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include <taler/taler_error_codes.h> 23 #include <taler/taler_dbevents.h> 24 #include <taler/taler_pq_lib.h> 25 #include "update_client.h" 26 #include "pg_helper.h" 27 28 enum GNUNET_DB_QueryStatus 29 CHALLENGERDB_update_client (struct CHALLENGERDB_PostgresContext *ctx, 30 uint64_t client_id, 31 const char *client_redirect_uri, 32 const char *client_secret, 33 bool *uri_conflict) 34 { 35 struct GNUNET_PQ_QueryParam params[] = { 36 GNUNET_PQ_query_param_uint64 (&client_id), 37 GNUNET_PQ_query_param_string (client_redirect_uri), 38 NULL == client_secret 39 ? GNUNET_PQ_query_param_null () 40 : GNUNET_PQ_query_param_string (client_secret), 41 GNUNET_PQ_query_param_end 42 }; 43 bool updated; 44 bool conflict; 45 struct GNUNET_PQ_ResultSpec rs[] = { 46 GNUNET_PQ_result_spec_bool ("updated", 47 &updated), 48 GNUNET_PQ_result_spec_bool ("uri_conflict", 49 &conflict), 50 GNUNET_PQ_result_spec_end 51 }; 52 enum GNUNET_DB_QueryStatus qs; 53 54 *uri_conflict = false; 55 /* "No rows updated" has two very different causes here: the client does 56 not exist, or another client already uses @a client_redirect_uri (which 57 violates clients_uri_key, and GNUnet maps a unique violation to 58 NO_RESULTS -- indistinguishable from "not found" at the API). So detect 59 the collision ourselves and refuse the UPDATE in that case, and report 60 which of the two happened. */ 61 PREPARE (ctx, 62 "update_client", 63 "WITH target AS (" 64 " SELECT client_serial_id" 65 " FROM clients" 66 " WHERE client_serial_id=$1" 67 "), taken AS (" 68 " SELECT client_serial_id" 69 " FROM clients" 70 " WHERE uri=$2" 71 " AND client_serial_id<>$1" 72 "), upd AS (" 73 " UPDATE clients" 74 " SET uri=$2" 75 " ,client_secret=COALESCE($3,client_secret)" 76 " WHERE client_serial_id=$1" 77 " AND NOT EXISTS (SELECT 1 FROM taken)" 78 " RETURNING client_serial_id" 79 ") SELECT" 80 " EXISTS (SELECT 1 FROM upd) AS updated" 81 " ,( EXISTS (SELECT 1 FROM target)" 82 " AND EXISTS (SELECT 1 FROM taken) ) AS uri_conflict;"); 83 qs = GNUNET_PQ_eval_prepared_singleton_select (ctx->conn, 84 "update_client", 85 params, 86 rs); 87 if (0 > qs) 88 return qs; 89 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 90 { 91 /* Our SELECT always yields exactly one row, so zero rows can only be a 92 unique violation on clients.uri: another transaction inserted the URI 93 after our snapshot was taken. */ 94 *uri_conflict = true; 95 return qs; 96 } 97 *uri_conflict = conflict; 98 return updated 99 ? GNUNET_DB_STATUS_SUCCESS_ONE_RESULT 100 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 101 }