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