pg_lookup_all_products.c (7359B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024, 2025 Taler Systems SA 4 5 TALER 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 TALER 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 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file backenddb/pg_lookup_all_products.c 18 * @brief Implementation of the lookup_all_products 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 "pg_lookup_all_products.h" 26 #include "pg_helper.h" 27 28 /** 29 * Context used for TMH_PG_lookup_all_products(). 30 */ 31 struct LookupProductsContext 32 { 33 /** 34 * Function to call with the results. 35 */ 36 TALER_MERCHANTDB_ProductCallback cb; 37 38 /** 39 * Closure for @a cb. 40 */ 41 void *cb_cls; 42 43 /** 44 * Postgres context. 45 */ 46 struct PostgresClosure *pg; 47 48 /** 49 * Did database result extraction fail? 50 */ 51 bool extract_failed; 52 }; 53 54 55 /** 56 * Function to be called with the results of a SELECT statement 57 * that has returned @a num_results results about products. 58 * 59 * @param[in,out] cls of type `struct LookupProductsContext *` 60 * @param result the postgres result 61 * @param num_results the number of results in @a result 62 */ 63 static void 64 lookup_products_cb (void *cls, 65 PGresult *result, 66 unsigned int num_results) 67 { 68 struct LookupProductsContext *plc = cls; 69 struct PostgresClosure *pg = plc->pg; 70 71 for (unsigned int i = 0; i < num_results; i++) 72 { 73 char *product_id; 74 uint64_t product_serial; 75 struct TALER_MERCHANTDB_ProductDetails pd; 76 size_t num_categories; 77 uint64_t *categories; 78 struct GNUNET_PQ_ResultSpec rs[] = { 79 GNUNET_PQ_result_spec_string ("product_id", 80 &product_id), 81 GNUNET_PQ_result_spec_uint64 ("product_serial", 82 &product_serial), 83 GNUNET_PQ_result_spec_string ("product_name", 84 &pd.product_name), 85 GNUNET_PQ_result_spec_string ("description", 86 &pd.description), 87 TALER_PQ_result_spec_json ("description_i18n", 88 &pd.description_i18n), 89 GNUNET_PQ_result_spec_string ("unit", 90 &pd.unit), 91 TALER_PQ_result_spec_amount_with_currency ("price", 92 &pd.price), 93 TALER_PQ_result_spec_array_amount_with_currency (pg->conn, 94 "price_array", 95 &pd.price_array_length, 96 &pd.price_array), 97 TALER_PQ_result_spec_json ("taxes", 98 &pd.taxes), 99 GNUNET_PQ_result_spec_uint64 ("total_stock", 100 &pd.total_stock), 101 GNUNET_PQ_result_spec_uint32 ("total_stock_frac", 102 &pd.total_stock_frac), 103 GNUNET_PQ_result_spec_bool ("allow_fractional_quantity", 104 &pd.allow_fractional_quantity), 105 GNUNET_PQ_result_spec_uint32 ("fractional_precision_level", 106 &pd.fractional_precision_level), 107 GNUNET_PQ_result_spec_uint64 ("total_sold", 108 &pd.total_sold), 109 GNUNET_PQ_result_spec_uint32 ("total_sold_frac", 110 &pd.total_sold_frac), 111 GNUNET_PQ_result_spec_uint64 ("total_lost", 112 &pd.total_lost), 113 GNUNET_PQ_result_spec_uint32 ("total_lost_frac", 114 &pd.total_lost_frac), 115 GNUNET_PQ_result_spec_string ("image", 116 &pd.image), 117 TALER_PQ_result_spec_json ("address", 118 &pd.address), 119 GNUNET_PQ_result_spec_timestamp ("next_restock", 120 &pd.next_restock), 121 GNUNET_PQ_result_spec_uint32 ("minimum_age", 122 &pd.minimum_age), 123 GNUNET_PQ_result_spec_array_uint64 (pg->conn, 124 "categories", 125 &num_categories, 126 &categories), 127 GNUNET_PQ_result_spec_end 128 }; 129 130 if (GNUNET_OK != 131 GNUNET_PQ_extract_result (result, 132 rs, 133 i)) 134 { 135 GNUNET_break (0); 136 plc->extract_failed = true; 137 return; 138 } 139 plc->cb (plc->cb_cls, 140 product_serial, 141 product_id, 142 &pd, 143 num_categories, 144 categories); 145 GNUNET_PQ_cleanup_result (rs); 146 } 147 } 148 149 150 enum GNUNET_DB_QueryStatus 151 TMH_PG_lookup_all_products (void *cls, 152 const char *instance_id, 153 TALER_MERCHANTDB_ProductCallback cb, 154 void *cb_cls) 155 { 156 struct PostgresClosure *pg = cls; 157 struct LookupProductsContext plc = { 158 .cb = cb, 159 .cb_cls = cb_cls, 160 .pg = pg, 161 /* Can be overwritten by the lookup_products_cb */ 162 .extract_failed = false, 163 }; 164 struct GNUNET_PQ_QueryParam params[] = { 165 GNUNET_PQ_query_param_string (instance_id), 166 GNUNET_PQ_query_param_end 167 }; 168 enum GNUNET_DB_QueryStatus qs; 169 170 check_connection (pg); 171 PREPARE (pg, 172 "lookup_all_products", 173 "SELECT" 174 " description" 175 ",description_i18n::TEXT" 176 ",product_name" 177 ",unit" 178 ",price" 179 ",price_array" 180 ",taxes::TEXT" 181 ",total_stock" 182 ",total_stock_frac" 183 ",allow_fractional_quantity" 184 ",fractional_precision_level" 185 ",total_sold" 186 ",total_sold_frac" 187 ",total_lost" 188 ",total_lost_frac" 189 ",image" 190 ",minv.address::TEXT" 191 ",next_restock" 192 ",minimum_age" 193 ",product_id" 194 ",product_serial" 195 ",t.category_array AS categories" 196 " FROM merchant_inventory minv" 197 " JOIN merchant_instances inst" 198 " USING (merchant_serial)" 199 ",LATERAL (" 200 " SELECT ARRAY (" 201 " SELECT mpc.category_serial" 202 " FROM merchant_product_categories mpc" 203 " WHERE mpc.product_serial = minv.product_serial" 204 " ) AS category_array" 205 " ) t" 206 " WHERE inst.merchant_id=$1"); 207 qs = GNUNET_PQ_eval_prepared_multi_select ( 208 pg->conn, 209 "lookup_all_products", 210 params, 211 &lookup_products_cb, 212 &plc); 213 /* If there was an error inside lookup_products_cb, return a hard error. */ 214 if (plc.extract_failed) 215 return GNUNET_DB_STATUS_HARD_ERROR; 216 return qs; 217 }