pg_lookup_all_products.c (7852B)
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 "taler/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_array_amount_with_currency (pg->conn, 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 TMH_PG_lookup_all_products (void *cls, 163 const char *instance_id, 164 TALER_MERCHANTDB_ProductCallback cb, 165 void *cb_cls) 166 { 167 struct PostgresClosure *pg = cls; 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_string (instance_id), 177 GNUNET_PQ_query_param_end 178 }; 179 enum GNUNET_DB_QueryStatus qs; 180 181 check_connection (pg); 182 PREPARE (pg, 183 "lookup_all_products", 184 "SELECT" 185 " description" 186 ",description_i18n::TEXT" 187 ",product_name" 188 ",unit" 189 ",price_array" 190 ",taxes::TEXT" 191 ",total_stock" 192 ",total_stock_frac" 193 ",allow_fractional_quantity" 194 ",fractional_precision_level" 195 ",total_sold" 196 ",total_sold_frac" 197 ",total_lost" 198 ",total_lost_frac" 199 ",image" 200 ",minv.address::TEXT" 201 ",next_restock" 202 ",minimum_age" 203 ",product_id" 204 ",product_serial" 205 ",t.category_array AS categories" 206 ",product_group_serial" 207 ",money_pot_serial" 208 ",price_is_net" 209 " FROM merchant_inventory minv" 210 " JOIN merchant_instances inst" 211 " USING (merchant_serial)" 212 ",LATERAL (" 213 " SELECT ARRAY (" 214 " SELECT mpc.category_serial" 215 " FROM merchant_product_categories mpc" 216 " WHERE mpc.product_serial = minv.product_serial" 217 " ) AS category_array" 218 " ) t" 219 " WHERE inst.merchant_id=$1"); 220 qs = GNUNET_PQ_eval_prepared_multi_select ( 221 pg->conn, 222 "lookup_all_products", 223 params, 224 &lookup_products_cb, 225 &plc); 226 /* If there was an error inside lookup_products_cb, return a hard error. */ 227 if (plc.extract_failed) 228 return GNUNET_DB_STATUS_HARD_ERROR; 229 return qs; 230 }