lookup_inventory_products_filtered.c (9461B)
1 /* 2 This file is part of TALER 3 Copyright (C) 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_inventory_products_filtered.c 18 * @brief Lookup inventory product details for templates (filtered) 19 * @author Bohdan Potuzhnyi 20 */ 21 #include "platform.h" 22 #include <taler/taler_pq_lib.h> 23 #include "merchant-database/lookup_inventory_products_filtered.h" 24 #include "helper.h" 25 26 27 /** 28 * Context used for TALER_MERCHANTDB_lookup_inventory_products_filtered(). 29 */ 30 struct LookupInventoryProductsContext 31 { 32 /** 33 * Function to call with the results. 34 */ 35 TALER_MERCHANTDB_InventoryProductCallback cb; 36 37 /** 38 * Closure for @a cb. 39 */ 40 void *cb_cls; 41 42 /** 43 * Postgres context for callbacks. 44 */ 45 struct TALER_MERCHANTDB_PostgresContext *pg; 46 47 /** 48 * Did database result extraction fail? 49 */ 50 bool extract_failed; 51 }; 52 53 54 static void 55 lookup_inventory_products_cb (void *cls, 56 PGresult *result, 57 unsigned int num_results) 58 { 59 struct LookupInventoryProductsContext *plc = cls; 60 struct TALER_MERCHANTDB_PostgresContext *pg = plc->pg; 61 62 for (unsigned int i = 0; i < num_results; i++) 63 { 64 char *product_id; 65 struct TALER_MERCHANTDB_InventoryProductDetails pd; 66 size_t num_categories; 67 uint64_t *categories; 68 bool no_image_hash; 69 struct GNUNET_PQ_ResultSpec rs[] = { 70 GNUNET_PQ_result_spec_string ("product_id", 71 &product_id), 72 GNUNET_PQ_result_spec_string ("product_name", 73 &pd.product_name), 74 GNUNET_PQ_result_spec_string ("description", 75 &pd.description), 76 TALER_PQ_result_spec_json ("description_i18n", 77 &pd.description_i18n), 78 GNUNET_PQ_result_spec_string ("unit", 79 &pd.unit), 80 TALER_PQ_result_spec_array_amount_with_currency (pg->conn, 81 "merchant", 82 "price_array", 83 &pd.price_array_length, 84 &pd.price_array), 85 GNUNET_PQ_result_spec_uint64 ("remaining_stock", 86 &pd.remaining_stock), 87 GNUNET_PQ_result_spec_uint32 ("remaining_stock_frac", 88 &pd.remaining_stock_frac), 89 TALER_PQ_result_spec_json ("taxes", 90 &pd.taxes), 91 GNUNET_PQ_result_spec_allow_null ( 92 GNUNET_PQ_result_spec_string ("image_hash", 93 &pd.image_hash), 94 &no_image_hash), 95 GNUNET_PQ_result_spec_bool ("allow_fractional_quantity", 96 &pd.allow_fractional_quantity), 97 GNUNET_PQ_result_spec_uint32 ("fractional_precision_level", 98 &pd.fractional_precision_level), 99 GNUNET_PQ_result_spec_array_uint64 (pg->conn, 100 "categories", 101 &num_categories, 102 &categories), 103 GNUNET_PQ_result_spec_end 104 }; 105 106 if (GNUNET_OK != 107 GNUNET_PQ_extract_result (result, 108 rs, 109 i)) 110 { 111 GNUNET_break (0); 112 plc->extract_failed = true; 113 return; 114 } 115 plc->cb (plc->cb_cls, 116 product_id, 117 &pd, 118 num_categories, 119 categories); 120 GNUNET_PQ_cleanup_result (rs); 121 } 122 } 123 124 125 enum GNUNET_DB_QueryStatus 126 TALER_MERCHANTDB_lookup_inventory_products_filtered ( 127 struct TALER_MERCHANTDB_PostgresContext *pg, 128 const char *instance_id, 129 const char *const *product_ids, 130 size_t num_product_ids, 131 const uint64_t *categories, 132 size_t num_categories, 133 TALER_MERCHANTDB_InventoryProductCallback cb, 134 void *cb_cls) 135 { 136 struct LookupInventoryProductsContext plc = { 137 .cb = cb, 138 .cb_cls = cb_cls, 139 .pg = pg, 140 /* Can be overwritten by the lookup_inventory_products_cb */ 141 .extract_failed = false, 142 }; 143 struct GNUNET_PQ_QueryParam params[] = { 144 (0 == num_product_ids) 145 ? GNUNET_PQ_query_param_null () 146 : GNUNET_PQ_query_param_array_ptrs_string ( 147 num_product_ids, 148 (const char **) product_ids, 149 pg->conn), 150 (0 == num_categories) 151 ? GNUNET_PQ_query_param_null () 152 : GNUNET_PQ_query_param_array_uint64 (num_categories, 153 categories, 154 pg->conn), 155 GNUNET_PQ_query_param_end 156 }; 157 enum GNUNET_DB_QueryStatus qs; 158 159 GNUNET_assert (NULL != pg->current_merchant_id); 160 GNUNET_assert (0 == strcmp (instance_id, 161 pg->current_merchant_id)); 162 TMH_PQ_prepare_anon (pg, 163 "SELECT" 164 " description" 165 ",description_i18n::TEXT" 166 ",product_name" 167 ",unit" 168 ",price_array" 169 ",CASE WHEN minv.total_stock = 9223372036854775807" 170 " THEN minv.total_stock" 171 " ELSE (GREATEST(0, rt.remaining_total) / 1000000)::INT8" 172 " END AS remaining_stock" 173 ",CASE WHEN minv.total_stock = 9223372036854775807" 174 " THEN 2147483647" 175 " ELSE mod(GREATEST(0, rt.remaining_total), 1000000)::INT4" 176 " END AS remaining_stock_frac" 177 ",taxes::TEXT" 178 ",image_hash" 179 ",allow_fractional_quantity" 180 ",fractional_precision_level" 181 ",product_id" 182 ",t.category_array AS categories" 183 " FROM merchant_inventory minv" 184 ",LATERAL (" 185 " SELECT ARRAY (" 186 " SELECT mpc.category_serial" 187 " FROM merchant_product_categories mpc" 188 " WHERE mpc.product_serial = minv.product_serial" 189 " ) AS category_array" 190 " ) t" 191 ",LATERAL (" 192 " SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" 193 " + total_locked_frac::NUMERIC), 0)" 194 " AS total_locked" 195 " FROM merchant_inventory_locks mil" 196 " WHERE mil.product_serial = minv.product_serial" 197 " ) il" 198 ",LATERAL (" 199 " SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" 200 " + total_locked_frac::NUMERIC), 0)" 201 " AS total_locked" 202 " FROM merchant_order_locks mol" 203 " WHERE mol.product_serial = minv.product_serial" 204 " ) ol" 205 ",LATERAL (" 206 " SELECT" 207 " (minv.total_stock::NUMERIC * 1000000" 208 " + minv.total_stock_frac::NUMERIC)" 209 " - (minv.total_sold::NUMERIC * 1000000" 210 " + minv.total_sold_frac::NUMERIC)" 211 " - (minv.total_lost::NUMERIC * 1000000" 212 " + minv.total_lost_frac::NUMERIC)" 213 " - il.total_locked" 214 " - ol.total_locked" 215 " AS remaining_total" 216 " ) rt" 217 " WHERE (" 218 " (COALESCE (array_length ($1::TEXT[], 1), 0) > 0" 219 " AND minv.product_id = ANY ($1::TEXT[]))" 220 " OR" 221 " (COALESCE (array_length ($2::BIGINT[], 1), 0) > 0" 222 " AND EXISTS (" 223 " SELECT 1" 224 " FROM merchant_product_categories mpc" 225 " WHERE mpc.product_serial = minv.product_serial" 226 " AND mpc.category_serial = ANY ($2::BIGINT[])" 227 " ))" 228 " )"); 229 qs = GNUNET_PQ_eval_prepared_multi_select ( 230 pg->conn, 231 "", 232 params, 233 &lookup_inventory_products_cb, 234 &plc); 235 GNUNET_PQ_cleanup_query_params_closures (params); 236 /* If there was an error inside lookup_inventory_products_cb, return a hard error. */ 237 if (plc.extract_failed) 238 return GNUNET_DB_STATUS_HARD_ERROR; 239 return qs; 240 }