diff options
author | Christian Grothoff <christian@grothoff.org> | 2020-04-20 13:39:18 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2020-04-20 13:39:18 +0200 |
commit | 64cc6eb8c6ed4b7867c76f57c7762414ab4d0685 (patch) | |
tree | 0fe7ae499fa9b43b85765192eb515358920d1ff6 /src | |
parent | 458dc3653b4aef54130c580fd41610aa03f61d68 (diff) | |
download | merchant-64cc6eb8c6ed4b7867c76f57c7762414ab4d0685.tar.gz merchant-64cc6eb8c6ed4b7867c76f57c7762414ab4d0685.tar.bz2 merchant-64cc6eb8c6ed4b7867c76f57c7762414ab4d0685.zip |
backenddb implementation work
Diffstat (limited to 'src')
6 files changed, 348 insertions, 26 deletions
diff --git a/src/backend/taler-merchant-httpd_private-get-products-ID.c b/src/backend/taler-merchant-httpd_private-get-products-ID.c index 85358ff8..57b3182c 100644 --- a/src/backend/taler-merchant-httpd_private-get-products-ID.c +++ b/src/backend/taler-merchant-httpd_private-get-products-ID.c @@ -79,8 +79,8 @@ TMH_private_get_products_ID (const struct TMH_RequestHandler *rh, (json_int_t) pd.total_lost, "description_i18n", pd.description_i18n, - "location", - pd.location, + "address", + pd.address, "image", pd.image); GNUNET_free (pd.description); diff --git a/src/backend/taler-merchant-httpd_private-get-products.c b/src/backend/taler-merchant-httpd_private-get-products.c index 8f70c9fe..21729a59 100644 --- a/src/backend/taler-merchant-httpd_private-get-products.c +++ b/src/backend/taler-merchant-httpd_private-get-products.c @@ -26,21 +26,18 @@ * Add product details to our JSON array. * * @param cls a `json_t *` JSON array to build - * @param key unused * @param product_id ID of the product * @param in_stock how many are currently in stock (possibly locked), -1 for infinite * @param unit in which unit is the stock measured in */ static void add_product (void *cls, - const struct GNUNET_HashCode *key, const char *product_id, long long in_stock, const char *unit) { json_t *pa = cls; - (void) key; GNUNET_assert (0 == json_array_append_new ( pa, diff --git a/src/backend/taler-merchant-httpd_private-patch-products-ID.c b/src/backend/taler-merchant-httpd_private-patch-products-ID.c index fd5a6c1e..6821d3d3 100644 --- a/src/backend/taler-merchant-httpd_private-patch-products-ID.c +++ b/src/backend/taler-merchant-httpd_private-patch-products-ID.c @@ -101,7 +101,7 @@ determine_cause (struct MHD_Connection *connection, GNUNET_free (pdx.unit); json_decref (pdx.taxes); json_decref (pdx.image); - json_decref (pdx.location); + json_decref (pdx.address); return TALER_MHD_reply_with_error (connection, MHD_HTTP_CONFLICT, ec, @@ -141,8 +141,8 @@ TMH_private_patch_products_ID (const struct TMH_RequestHandler *rh, &pd.image), GNUNET_JSON_spec_json ("taxes", &pd.taxes), - GNUNET_JSON_spec_json ("location", - &pd.taxes), + GNUNET_JSON_spec_json ("address", + &pd.address), GNUNET_JSON_spec_int64 ("total_stocked", &total_stocked), GNUNET_JSON_spec_absolute_time ("next_restock", diff --git a/src/backend/taler-merchant-httpd_private-post-products.c b/src/backend/taler-merchant-httpd_private-post-products.c index 498e51c9..c4402841 100644 --- a/src/backend/taler-merchant-httpd_private-post-products.c +++ b/src/backend/taler-merchant-httpd_private-post-products.c @@ -61,8 +61,8 @@ products_equal (const struct TALER_MERCHANTDB_ProductDetails *p1, (p1->total_lost == p2->total_lost) && (1 == json_equal (p1->image, p2->image)) && - (1 == json_equal (p1->location, - p2->location)) && + (1 == json_equal (p1->address, + p2->address)) && (p1->next_restock.abs_value_us == p2->next_restock.abs_value_us) ); } @@ -101,8 +101,8 @@ TMH_private_post_products (const struct TMH_RequestHandler *rh, &pd.image), GNUNET_JSON_spec_json ("taxes", &pd.taxes), - GNUNET_JSON_spec_json ("location", - &pd.taxes), + GNUNET_JSON_spec_json ("address", + &pd.address), GNUNET_JSON_spec_int64 ("total_stocked", &total_stocked), GNUNET_JSON_spec_absolute_time ("next_restock", diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index 70a98d5c..08f80930 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -666,8 +666,8 @@ postgres_purge_instance (void *cls, * @return database result code */ static enum GNUNET_DB_QueryStatus -postgres_patch_instance (void *cls, - const struct TALER_MERCHANTDB_InstanceSettings *is) +postgres_update_instance (void *cls, + const struct TALER_MERCHANTDB_InstanceSettings *is) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -715,6 +715,327 @@ postgres_inactivate_account (void *cls, } +/** + * Context used for postgres_lookup_products(). + */ +struct LookupProductsContext +{ + /** + * Function to call with the results. + */ + TALER_MERCHANTDB_ProductsCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Internal result. + */ + enum GNUNET_DB_QueryStatus qs; +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results about products. + * + * @param[in,out] cls of type `struct LookupProductsContext *` + * @param result the postgres result + * @param num_result the number of results in @a result + */ +static void +lookup_products_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct LookupProductsContext *plc = cls; + + for (unsigned int i = 0; i < num_results; i++) + { + char *product_id; + uint64_t in_stock; + char *unit; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_string ("product_id", + &product_id), + GNUNET_PQ_result_spec_uint64 ("in_stock", + &in_stock), + GNUNET_PQ_result_spec_string ("unit", + &unit), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + plc->qs = GNUNET_DB_STATUS_HARD_ERROR; + return; + } + plc->cb (plc->cb_cls, + product_id, + (UINT64_MAX == in_stock) ? -1LL : (long long) in_stock, + unit); + GNUNET_PQ_cleanup_result (rs); + } +} + + +/** + * Lookup all of the products the given instance has configured. + * + * @param cls closure + * @param instance_id instance to lookup products for + * @param cb function to call on all products found + * @param cb_cls closure for @a cb + * @return database result code + */ +static enum GNUNET_DB_QueryStatus +postgres_lookup_products (void *cls, + const char *instance_id, + TALER_MERCHANTDB_ProductsCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pg = cls; + struct LookupProductsContext plc = { + .cb = cb, + .cb_cls = cb_cls + }; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "lookup_products", + params, + &lookup_products_cb, + &plc); + if (0 != plc.qs) + return plc.qs; + return qs; +} + + +/** + * Lookup details about a particular product. + * + * @param cls closure + * @param instance_id instance to lookup products for + * @param product_id product to lookup + * @param[out] pd set to the product details on success, can be NULL + * (in that case we only want to check if the product exists) + * @return database result code + */ +static enum GNUNET_DB_QueryStatus +postgres_lookup_product (void *cls, + const char *instance_id, + const char *product_id, + struct TALER_MERCHANTDB_ProductDetails *pd) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (product_id), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_string ("description", + &pd->description), + TALER_PQ_result_spec_json ("description_i18n", + &pd->description_i18n), + GNUNET_PQ_result_spec_string ("unit", + &pd->unit), + TALER_PQ_RESULT_SPEC_AMOUNT ("price", + &pd->price), + TALER_PQ_result_spec_json ("taxes", + &pd->taxes), + GNUNET_PQ_result_spec_uint64 ("total_stocked", + &pd->total_stocked), + GNUNET_PQ_result_spec_uint64 ("total_sold", + &pd->total_sold), + GNUNET_PQ_result_spec_uint64 ("total_lost", + &pd->total_lost), + TALER_PQ_result_spec_json ("image", + &pd->image), + TALER_PQ_result_spec_json ("address", + &pd->address), + GNUNET_PQ_result_spec_absolute_time ("next_restock", + &pd->next_restock), + GNUNET_PQ_result_spec_end + }; + struct GNUNET_PQ_ResultSpec rs_null[] = { + GNUNET_PQ_result_spec_end + }; + + check_connection (pg); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "lookup_product", + params, + (NULL == pd) + ? rs_null + : rs); +} + + +/** + * Delete information about a product. Note that the transaction must + * enforce that no stocks are currently locked. + * + * @param cls closure + * @param instance_id instance to delete product of + * @param product_id product to delete + * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS + * if locks prevent deletion OR product unknown + */ +static enum GNUNET_DB_QueryStatus +postgres_delete_product (void *cls, + const char *instance_id, + const char *product_id) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (product_id), + GNUNET_PQ_query_param_end + }; + + check_connection (pg); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "delete_product", + params); +} + + +/** + * Insert details about a particular product. + * + * @param cls closure + * @param instance_id instance to insert product for + * @param product_id product identifier of product to insert + * @param pd the product details to insert + * @return database result code + */ +static enum GNUNET_DB_QueryStatus +postgres_insert_product (void *cls, + const char *instance_id, + const char *product_id, + const struct TALER_MERCHANTDB_ProductDetails *pd) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (product_id), + GNUNET_PQ_query_param_string (pd->description), + TALER_PQ_query_param_json (pd->description_i18n), + GNUNET_PQ_query_param_string (pd->unit), + TALER_PQ_query_param_json (pd->image), + TALER_PQ_query_param_json (pd->taxes), + TALER_PQ_query_param_amount (&pd->price), + GNUNET_PQ_query_param_uint64 (&pd->total_stocked), + GNUNET_PQ_query_param_uint64 (&pd->total_sold), + GNUNET_PQ_query_param_uint64 (&pd->total_lost), + TALER_PQ_query_param_json (pd->address), + GNUNET_PQ_query_param_absolute_time (&pd->next_restock), + GNUNET_PQ_query_param_end + }; + + check_connection (pg); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_product", + params); +} + + +/** + * Update details about a particular product. Note that the + * transaction must enforce that the sold/stocked/lost counters + * are not reduced (i.e. by expanding the WHERE clause on the existing + * values). + * + * @param cls closure + * @param instance_id instance to lookup products for + * @param product_id product to lookup + * @param[out] pd set to the product details on success, can be NULL + * (in that case we only want to check if the product exists) + * @return database result code, #GNUNET_DB_SUCCESS_NO_RESULTS if the + * non-decreasing constraints are not met *or* if the product + * does not yet exist. + */ +static enum GNUNET_DB_QueryStatus +postgres_update_product (void *cls, + const char *instance_id, + const char *product_id, + struct TALER_MERCHANTDB_ProductDetails *pd) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (product_id), + GNUNET_PQ_query_param_string (pd->description), + TALER_PQ_query_param_json (pd->description_i18n), + GNUNET_PQ_query_param_string (pd->unit), + TALER_PQ_query_param_json (pd->image), + TALER_PQ_query_param_json (pd->taxes), + TALER_PQ_query_param_amount (&pd->price), + GNUNET_PQ_query_param_uint64 (&pd->total_stocked), + GNUNET_PQ_query_param_uint64 (&pd->total_sold), + GNUNET_PQ_query_param_uint64 (&pd->total_lost), + TALER_PQ_query_param_json (pd->address), + GNUNET_PQ_query_param_absolute_time (&pd->next_restock), + GNUNET_PQ_query_param_end + }; + + check_connection (pg); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "update_product", + params); +} + + +/** + * Lock stocks of a particular product. Note that the transaction must + * enforce that the "stocked-sold-lost >= locked" constraint holds. + * + * @param cls closure + * @param instance_id instance to lookup products for + * @param product_id product to lookup + * @param uuid the UUID that holds the lock + * @param quantity how many units should be locked + * @param expiration_time when should the lock expire + * @return database result code, #GNUNET_DB_SUCCESS_NO_RESULTS if the + * product is unknown OR if there insufficient stocks remaining + */ +static enum GNUNET_DB_QueryStatus +postgres_lock_product (void *cls, + const char *instance_id, + const char *product_id, + const struct GNUNET_Uuid *uuid, + uint32_t quantity, + struct GNUNET_TIME_Absolute expiration_time) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (product_id), + GNUNET_PQ_query_param_auto_from_type (uuid), + GNUNET_PQ_query_param_uint32 (&quantity), + GNUNET_PQ_query_param_absolute_time (&expiration_time), + GNUNET_PQ_query_param_end + }; + + check_connection (pg); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "lock_product", + params); +} + + /* ********************* OLD API ************************** */ /** @@ -727,12 +1048,11 @@ postgres_inactivate_account (void *cls, * @return transaction status */ static enum GNUNET_DB_QueryStatus -postgres_find_contract_terms_from_hash (void *cls, - json_t **contract_terms, - const struct - GNUNET_HashCode *h_contract_terms, - const struct - TALER_MerchantPublicKeyP *merchant_pub) +postgres_find_contract_terms_from_hash ( + void *cls, + json_t **contract_terms, + const struct GNUNET_HashCode *h_contract_terms, + const struct TALER_MerchantPublicKeyP *merchant_pub) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -3677,7 +3997,7 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) "DELETE FROM merchant_instances" " WHERE merchant_instances.merchant_id = $1", 1), - /* for postgres_patch_instance() */ + /* for postgres_update_instance() */ GNUNET_PQ_make_prepare ("update_instance", "UPDATE merchant_instances SET" " merchant_name=$2" @@ -4199,8 +4519,14 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) plugin->insert_account = &postgres_insert_account; plugin->delete_instance_private_key = &postgres_delete_instance_private_key; plugin->purge_instance = &postgres_purge_instance; - plugin->patch_instance = &postgres_patch_instance; + plugin->update_instance = &postgres_update_instance; plugin->inactivate_account = &postgres_inactivate_account; + plugin->lookup_products = &postgres_lookup_products; + plugin->lookup_product = &postgres_lookup_product; + plugin->delete_product = &postgres_delete_product; + plugin->insert_product = &postgres_insert_product; + plugin->update_product = &postgres_update_product; + plugin->lock_product = &postgres_lock_product; /* old API: */ plugin->store_deposit = &postgres_store_deposit; diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index 353997db..09bb1372 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -144,14 +144,12 @@ typedef void * Typically called by `lookup_products`. * * @param cls a `json_t *` JSON array to build - * @param key unused * @param product_id ID of the product * @param in_stock how many are currently in stock (possibly locked), -1 for infinite * @param unit in which unit is the stock measured in */ typedef void (*TALER_MERCHANTDB_ProductsCallback)(void *cls, - const struct GNUNET_HashCode *key, const char *product_id, long long in_stock, const char *unit); @@ -214,7 +212,7 @@ struct TALER_MERCHANTDB_ProductDetails /** * Identifies where the product is in stock, possibly an empty map. */ - json_t *location; + json_t *address; /** * Identifies when the product will be restocked. 0 for unknown, @@ -542,7 +540,8 @@ struct TALER_MERCHANTDB_Plugin struct TALER_MERCHANTDB_ProductDetails *pd); /** - * Delete information about a product. + * Delete information about a product. Note that the transaction must + * enforce that no stocks are currently locked. * * @param cls closure * @param instance_id instance to delete product of |