/* This file is part of TALER Copyright (C) 2022,2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. TALER is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, see */ /** * @file backenddb/pg_lookup_orders.c * @brief Implementation of the lookup_orders function for Postgres * @author Iván Ávalos * @author Christian Grothoff */ #include "platform.h" #include #include #include #include "pg_lookup_orders.h" #include "pg_helper.h" /** * Context used for TMH_PG_lookup_orders(). */ struct LookupOrdersContext { /** * Function to call with the results. */ TALER_MERCHANTDB_OrdersCallback cb; /** * Closure for @a cb. */ void *cb_cls; /** * Did database result extraction fail? */ bool extract_failed; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results about orders. * * @param[in,out] cls of type `struct LookupOrdersContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_orders_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupOrdersContext *plc = cls; for (unsigned int i = 0; i < num_results; i++) { char *order_id; uint64_t order_serial; struct GNUNET_TIME_Timestamp ts; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_string ("order_id", &order_id), GNUNET_PQ_result_spec_uint64 ("order_serial", &order_serial), GNUNET_PQ_result_spec_timestamp ("creation_time", &ts), GNUNET_PQ_result_spec_end }; if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, i)) { GNUNET_break (0); plc->extract_failed = true; return; } plc->cb (plc->cb_cls, order_id, order_serial, ts); GNUNET_PQ_cleanup_result (rs); } } enum GNUNET_DB_QueryStatus TMH_PG_lookup_orders (void *cls, const char *instance_id, const struct TALER_MERCHANTDB_OrderFilter *of, TALER_MERCHANTDB_OrdersCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupOrdersContext plc = { .cb = cb, .cb_cls = cb_cls }; uint64_t limit = (of->delta > 0) ? of->delta : -of->delta; struct GNUNET_PQ_QueryParam params[] = { /* $1 */ GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&limit), GNUNET_PQ_query_param_uint64 (&of->start_row), GNUNET_PQ_query_param_timestamp (&of->date), GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == of->paid)), /* $6 */ GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == of->paid)), GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == of->refunded)), GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == of->refunded)), GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == of->wired)), GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == of->wired)), /* $11 */ GNUNET_PQ_query_param_bool (NULL == of->session_id), NULL == of->session_id ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_string (of->session_id), GNUNET_PQ_query_param_bool (NULL == of->fulfillment_url), NULL == of->fulfillment_url ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_string (of->fulfillment_url), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; char stmt[128]; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Looking up orders, using filter paid:%d, refunded: %d, wired: %d\n", of->paid, of->refunded, of->wired); GNUNET_snprintf (stmt, sizeof (stmt), "lookup_orders_%s", (of->delta > 0) ? "inc" : "dec"); PREPARE (pg, "lookup_orders_dec", "(SELECT" " order_id" ",order_serial" ",creation_time" " FROM merchant_orders" " WHERE merchant_orders.merchant_serial=" " (SELECT merchant_serial " " FROM merchant_instances" " WHERE merchant_id=$1)" " AND" " order_serial < $3" " AND" " creation_time < $4" " AND ($5 OR " " NOT CAST($6 AS BOOL))" /* unclaimed orders are never paid */ " AND ($7 OR " " NOT CAST($8 AS BOOL))"/* unclaimed orders are never refunded */ " AND ($9 OR " " NOT CAST($10 AS BOOL))" /* unclaimed orders are never wired */ " AND" " order_serial NOT IN" " (SELECT order_serial" " FROM merchant_contract_terms)" /* only select unclaimed orders */ " AND ($11 OR " " ($12 = session_id))" " AND ($13 OR " " ($14 = fulfillment_url))" " ORDER BY order_serial DESC" " LIMIT $2)" "UNION " /* union ensures elements are distinct! */ "(SELECT" " order_id" ",order_serial" ",creation_time" " FROM merchant_contract_terms" " WHERE merchant_contract_terms.merchant_serial=" " (SELECT merchant_serial " " FROM merchant_instances" " WHERE merchant_id=$1)" " AND" " order_serial < $3" " AND" " creation_time < $4" " AND ($5 OR " " (CAST($6 AS BOOL) = paid))" " AND ($7 OR " " (CAST($8 AS BOOL) = (order_serial IN" " (SELECT order_serial " " FROM merchant_refunds))))" " AND ($9 OR" " (CAST($10 AS BOOL) = wired))" " AND ($11 OR " " ($12 = session_id))" " AND ($13 OR " " ($14 = fulfillment_url))" " ORDER BY order_serial DESC" " LIMIT $2)" " ORDER BY order_serial DESC" " LIMIT $2"); PREPARE (pg, "lookup_orders_inc", "(SELECT" " order_id" ",order_serial" ",creation_time" " FROM merchant_orders" " WHERE merchant_orders.merchant_serial=" " (SELECT merchant_serial " " FROM merchant_instances" " WHERE merchant_id=$1)" " AND" " order_serial > $3" " AND" " creation_time > $4" " AND ($5 OR " " NOT CAST($6 AS BOOL))" /* unclaimed orders are never paid */ " AND ($7 OR " " NOT CAST($8 AS BOOL))"/* unclaimed orders are never refunded */ " AND ($9 OR " " NOT CAST($10 AS BOOL))" /* unclaimed orders are never wired */ " AND" " (order_serial NOT IN" " (SELECT order_serial" " FROM merchant_contract_terms))" /* only select unclaimed orders */ " AND ($11 OR " " ($12 = session_id))" " AND ($13 OR " " ($14 = fulfillment_url))" " ORDER BY order_serial ASC" " LIMIT $2)" "UNION " /* union ensures elements are distinct! */ "(SELECT" " order_id" ",order_serial" ",creation_time" " FROM merchant_contract_terms" " WHERE merchant_contract_terms.merchant_serial=" " (SELECT merchant_serial " " FROM merchant_instances" " WHERE merchant_id=$1)" " AND" " order_serial > $3" " AND" " creation_time > $4" " AND ($5 OR " " (CAST($6 AS BOOL) = paid))" " AND ($7 OR " " (CAST($8 AS BOOL) = (order_serial IN" " (SELECT order_serial " " FROM merchant_refunds))))" " AND ($9 OR" " (CAST($10 AS BOOL) = wired))" " AND ($11 OR " " ($12 = session_id))" " AND ($13 OR " " ($14 = fulfillment_url))" " ORDER BY order_serial ASC" " LIMIT $2)" " ORDER BY order_serial ASC" " LIMIT $2"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, stmt, params, &lookup_orders_cb, &plc); if (plc.extract_failed) return GNUNET_DB_STATUS_HARD_ERROR; return qs; }