From f7c320990c64ec5f29dc87340c24d5d9ebf7a835 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 25 Oct 2020 22:32:14 +0100 Subject: implement #6524 --- src/include/gnunet_pq_lib.h | 55 ++++++++++++++++++- src/pq/pq.c | 45 +++++++--------- src/pq/pq_query_helper.c | 126 +++++++++++++++++++++----------------------- src/pq/pq_result_helper.c | 101 +++++++---------------------------- src/pq/test_pq.c | 17 ++++-- 5 files changed, 163 insertions(+), 181 deletions(-) (limited to 'src') diff --git a/src/include/gnunet_pq_lib.h b/src/include/gnunet_pq_lib.h index ca549f77c..de717526c 100644 --- a/src/include/gnunet_pq_lib.h +++ b/src/include/gnunet_pq_lib.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet - Copyright (C) 2016, 2017 GNUnet e.V. + Copyright (C) 2016, 2017, 2020 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -87,6 +87,7 @@ struct GNUNET_PQ_QueryParam * Number of parameters eaten by this operation. */ unsigned int num_params; + }; @@ -99,21 +100,33 @@ struct GNUNET_PQ_QueryParam } +/** + * Generate query parameter to create a NULL value. + * + * @return query parameter to use to insert NULL into DB + */ +struct GNUNET_PQ_QueryParam +GNUNET_PQ_query_param_null (void); + + /** * Generate query parameter for a buffer @a ptr of * @a ptr_size bytes. * * @param ptr pointer to the query parameter to pass * @oaran ptr_size number of bytes in @a ptr + * @return query parameter to use */ struct GNUNET_PQ_QueryParam -GNUNET_PQ_query_param_fixed_size (const void *ptr, size_t ptr_size); +GNUNET_PQ_query_param_fixed_size (const void *ptr, + size_t ptr_size); /** * Generate query parameter for a string. * * @param ptr pointer to the string query parameter to pass + * @return query parameter to use */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_string (const char *ptr); @@ -124,6 +137,7 @@ GNUNET_PQ_query_param_string (const char *ptr); * by variable type. * * @param x pointer to the query parameter to pass. + * @return query parameter to use */ #define GNUNET_PQ_query_param_auto_from_type(x) \ GNUNET_PQ_query_param_fixed_size ((x), sizeof(*(x))) @@ -134,6 +148,7 @@ GNUNET_PQ_query_param_string (const char *ptr); * database must contain a BLOB type in the respective position. * * @param x the query parameter to pass. + * @return query parameter to use */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_rsa_public_key ( @@ -145,6 +160,7 @@ GNUNET_PQ_query_param_rsa_public_key ( * database must contain a BLOB type in the respective position. * * @param x the query parameter to pass + * @return query parameter to use */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_rsa_signature ( @@ -156,6 +172,7 @@ GNUNET_PQ_query_param_rsa_signature ( * The database must store a 64-bit integer. * * @param x pointer to the query parameter to pass + * @return query parameter to use */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_relative_time (const struct GNUNET_TIME_Relative *x); @@ -166,6 +183,7 @@ GNUNET_PQ_query_param_relative_time (const struct GNUNET_TIME_Relative *x); * The database must store a 64-bit integer. * * @param x pointer to the query parameter to pass + * @return query parameter to use */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x); @@ -176,6 +194,7 @@ GNUNET_PQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x); * The database must store a 64-bit integer. * * @param x pointer to the query parameter to pass + * @return query parameter to use */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_absolute_time_nbo ( @@ -186,6 +205,7 @@ GNUNET_PQ_query_param_absolute_time_nbo ( * Generate query parameter for an uint16_t in host byte order. * * @param x pointer to the query parameter to pass + * @return query parameter to use */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_uint16 (const uint16_t *x); @@ -195,6 +215,7 @@ GNUNET_PQ_query_param_uint16 (const uint16_t *x); * Generate query parameter for an uint32_t in host byte order. * * @param x pointer to the query parameter to pass + * @return query parameter to use */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_uint32 (const uint32_t *x); @@ -204,6 +225,7 @@ GNUNET_PQ_query_param_uint32 (const uint32_t *x); * Generate query parameter for an uint16_t in host byte order. * * @param x pointer to the query parameter to pass + * @return query parameter to use */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_uint64 (const uint64_t *x); @@ -288,6 +310,20 @@ struct GNUNET_PQ_ResultSpec * Where to store actual size of the result. */ size_t *result_size; + + /** + * True if NULL is allowed for a value in the database. + */ + bool is_nullable; + + /** + * Points to a location where we should store + * "true" if the result found is NULL, and + * otherwise "false". Only used if @e is_nullable + * is true. + */ + bool *is_null; + }; @@ -302,6 +338,21 @@ struct GNUNET_PQ_ResultSpec } +/** + * Allow NULL value to be found in the database + * for the given value. + * + * @param rs result spec entry to modify + * @param[out] is_null location set to 'true' if the + * value was indeed NULL, set to 'false' if the + * value was non-NULL + * @return array entry for the result specification to use + */ +struct GNUNET_PQ_ResultSpec +GNUNET_PQ_result_spec_allow_null (struct GNUNET_PQ_ResultSpec rs, + bool *is_null); + + /** * Variable-size result expected. * diff --git a/src/pq/pq.c b/src/pq/pq.c index eca097e58..e9c960e33 100644 --- a/src/pq/pq.c +++ b/src/pq/pq.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - Copyright (C) 2014, 2015, 2016, 2017, 2019 GNUnet e.V. + Copyright (C) 2014, 2015, 2016, 2017, 2019, 2020 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -27,14 +27,7 @@ #include "platform.h" #include "pq.h" -/** - * Execute a prepared statement. - * - * @param db database handle - * @param name name of the prepared statement - * @param params parameters to the statement - * @return postgres result - */ + PGresult * GNUNET_PQ_exec_prepared (struct GNUNET_PQ_Context *db, const char *name, @@ -120,12 +113,6 @@ GNUNET_PQ_exec_prepared (struct GNUNET_PQ_Context *db, } -/** - * Free all memory that was allocated in @a rs during - * #GNUNET_PQ_extract_result(). - * - * @param rs reult specification to clean up - */ void GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs) { @@ -136,17 +123,6 @@ GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs) } -/** - * Extract results from a query result according to the given - * specification. - * - * @param result result to process - * @param[in,out] rs result specification to extract for - * @param row row from the result to extract - * @return - * #GNUNET_YES if all results could be extracted - * #GNUNET_SYSERR if a result was invalid (non-existing field) - */ int GNUNET_PQ_extract_result (PGresult *result, struct GNUNET_PQ_ResultSpec *rs, @@ -160,6 +136,23 @@ GNUNET_PQ_extract_result (PGresult *result, int ret; spec = &rs[i]; + if (spec->is_nullable) + { + int fnum; + + fnum = PQfnumber (result, + spec->fname); + if (PQgetisnull (result, + row, + fnum)) + { + if (NULL != spec->is_null) + *spec->is_null = true; + continue; + } + if (NULL != spec->is_null) + *spec->is_null = false; + } ret = spec->conv (spec->cls, result, row, diff --git a/src/pq/pq_query_helper.c b/src/pq/pq_query_helper.c index a36848f3a..cee84d203 100644 --- a/src/pq/pq_query_helper.c +++ b/src/pq/pq_query_helper.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - Copyright (C) 2014, 2015, 2016 GNUnet e.V. + Copyright (C) 2014, 2015, 2016, 2020 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -27,6 +27,56 @@ #include "gnunet_pq_lib.h" +/** + * Function called to convert input argument into SQL parameters. + * + * @param cls closure + * @param data pointer to input argument + * @param data_len number of bytes in @a data (if applicable) + * @param[out] param_values SQL data to set + * @param[out] param_lengths SQL length data to set + * @param[out] param_formats SQL format data to set + * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays + * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() + * @param scratch_length number of entries left in @a scratch + * @return -1 on error, number of offsets used in @a scratch otherwise + */ +static int +qconv_null (void *cls, + const void *data, + size_t data_len, + void *param_values[], + int param_lengths[], + int param_formats[], + unsigned int param_length, + void *scratch[], + unsigned int scratch_length) +{ + (void) scratch; + (void) scratch_length; + (void) data; + (void) data_len; + GNUNET_break (NULL == cls); + if (1 != param_length) + return -1; + param_values[0] = NULL; + param_lengths[0] = 0; + param_formats[0] = 1; + return 0; +} + + +struct GNUNET_PQ_QueryParam +GNUNET_PQ_query_param_null (void) +{ + struct GNUNET_PQ_QueryParam res = { + &qconv_null, NULL, NULL, 0, 1 + }; + + return res; +} + + /** * Function called to convert input argument into SQL parameters. * @@ -64,33 +114,23 @@ qconv_fixed (void *cls, } -/** - * Generate query parameter for a buffer @a ptr of - * @a ptr_size bytes. - * - * @param ptr pointer to the query parameter to pass - * @oaran ptr_size number of bytes in @a ptr - */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_fixed_size (const void *ptr, size_t ptr_size) { - struct GNUNET_PQ_QueryParam res = - { &qconv_fixed, NULL, ptr, ptr_size, 1 }; + struct GNUNET_PQ_QueryParam res = { + &qconv_fixed, NULL, ptr, ptr_size, 1 + }; return res; } -/** - * Generate query parameter for a string. - * - * @param ptr pointer to the string query parameter to pass - */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_string (const char *ptr) { - return GNUNET_PQ_query_param_fixed_size (ptr, strlen (ptr)); + return GNUNET_PQ_query_param_fixed_size (ptr, + strlen (ptr)); } @@ -137,11 +177,6 @@ qconv_uint16 (void *cls, } -/** - * Generate query parameter for an uint16_t in host byte order. - * - * @param x pointer to the query parameter to pass - */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_uint16 (const uint16_t *x) { @@ -195,11 +230,6 @@ qconv_uint32 (void *cls, } -/** - * Generate query parameter for an uint32_t in host byte order. - * - * @param x pointer to the query parameter to pass - */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_uint32 (const uint32_t *x) { @@ -253,11 +283,6 @@ qconv_uint64 (void *cls, } -/** - * Generate query parameter for an uint64_t in host byte order. - * - * @param x pointer to the query parameter to pass - */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_uint64 (const uint64_t *x) { @@ -310,13 +335,6 @@ qconv_rsa_public_key (void *cls, } -/** - * Generate query parameter for an RSA public key. The - * database must contain a BLOB type in the respective position. - * - * @param x the query parameter to pass - * @return array entry for the query parameters to use - */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_rsa_public_key (const struct GNUNET_CRYPTO_RsaPublicKey *x) @@ -370,13 +388,6 @@ qconv_rsa_signature (void *cls, } -/** - * Generate query parameter for an RSA signature. The - * database must contain a BLOB type in the respective position. - * - * @param x the query parameter to pass - * @return array entry for the query parameters to use - */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_rsa_signature (const struct GNUNET_CRYPTO_RsaSignature *x) { @@ -432,13 +443,6 @@ qconv_rel_time (void *cls, } -/** - * Generate query parameter for a relative time value. - * The database must store a 64-bit integer. - * - * @param x pointer to the query parameter to pass - * @return array entry for the query parameters to use - */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_relative_time (const struct GNUNET_TIME_Relative *x) { @@ -494,29 +498,17 @@ qconv_abs_time (void *cls, } -/** - * Generate query parameter for an absolute time value. - * The database must store a 64-bit integer. - * - * @param x pointer to the query parameter to pass - * @return array entry for the query parameters to use - */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x) { - struct GNUNET_PQ_QueryParam res = - { &qconv_abs_time, NULL, x, sizeof(*x), 1 }; + struct GNUNET_PQ_QueryParam res = { + &qconv_abs_time, NULL, x, sizeof(*x), 1 + }; return res; } -/** - * Generate query parameter for an absolute time value. - * The database must store a 64-bit integer. - * - * @param x pointer to the query parameter to pass - */ struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_absolute_time_nbo (const struct GNUNET_TIME_AbsoluteNBO *x) diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c index f764593b0..546822e45 100644 --- a/src/pq/pq_result_helper.c +++ b/src/pq/pq_result_helper.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - Copyright (C) 2014, 2015, 2016 GNUnet e.V. + Copyright (C) 2014, 2015, 2016, 2020 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -27,6 +27,19 @@ #include "gnunet_pq_lib.h" +struct GNUNET_PQ_ResultSpec +GNUNET_PQ_result_spec_allow_null (struct GNUNET_PQ_ResultSpec rs, + bool *is_null) +{ + struct GNUNET_PQ_ResultSpec rsr; + + rsr = rs; + rsr.is_nullable = true; + rsr.is_null = is_null; + return rsr; +} + + /** * Function called to clean up memory allocated * by a #GNUNET_PQ_ResultConverter. @@ -112,14 +125,6 @@ extract_varsize_blob (void *cls, } -/** - * Variable-size result expected. - * - * @param name name of the field in the table - * @param[out] dst where to store the result, allocated - * @param[out] sptr where to store the size of @a dst - * @return array entry for the result specification to use - */ struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_variable_size (const char *name, void **dst, @@ -196,14 +201,6 @@ extract_fixed_blob (void *cls, } -/** - * Fixed-size result expected. - * - * @param name name of the field in the table - * @param[out] dst where to store the result - * @param dst_size number of bytes in @a dst - * @return array entry for the result specification to use - */ struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_fixed_size (const char *name, void *dst, @@ -301,13 +298,6 @@ clean_rsa_public_key (void *cls, } -/** - * RSA public key expected. - * - * @param name name of the field in the table - * @param[out] rsa where to store the result - * @return array entry for the result specification to use - */ struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_rsa_public_key (const char *name, struct GNUNET_CRYPTO_RsaPublicKey **rsa) @@ -405,13 +395,6 @@ clean_rsa_signature (void *cls, } -/** - * RSA signature expected. - * - * @param name name of the field in the table - * @param[out] sig where to store the result; - * @return array entry for the result specification to use - */ struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_rsa_signature (const char *name, struct GNUNET_CRYPTO_RsaSignature **sig) @@ -509,13 +492,6 @@ clean_string (void *cls, } -/** - * 0-terminated string expected. - * - * @param name name of the field in the table - * @param[out] dst where to store the result, allocated - * @return array entry for the result specification to use - */ struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_string (const char *name, char **dst) @@ -595,13 +571,6 @@ extract_rel_time (void *cls, } -/** - * Relative time expected. - * - * @param name name of the field in the table - * @param[out] at where to store the result - * @return array entry for the result specification to use - */ struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_relative_time (const char *name, struct GNUNET_TIME_Relative *rt) @@ -685,13 +654,6 @@ extract_abs_time (void *cls, } -/** - * Absolute time expected. - * - * @param name name of the field in the table - * @param[out] at where to store the result - * @return array entry for the result specification to use - */ struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_absolute_time (const char *name, struct GNUNET_TIME_Absolute *at) @@ -706,13 +668,6 @@ GNUNET_PQ_result_spec_absolute_time (const char *name, } -/** - * Absolute time in network byte order expected. - * - * @param name name of the field in the table - * @param[out] at where to store the result - * @return array entry for the result specification to use - */ struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_absolute_time_nbo (const char *name, struct GNUNET_TIME_AbsoluteNBO *at) @@ -786,13 +741,6 @@ extract_uint16 (void *cls, } -/** - * uint16_t expected. - * - * @param name name of the field in the table - * @param[out] u16 where to store the result - * @return array entry for the result specification to use - */ struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_uint16 (const char *name, uint16_t *u16) @@ -869,13 +817,6 @@ extract_uint32 (void *cls, } -/** - * uint32_t expected. - * - * @param name name of the field in the table - * @param[out] u32 where to store the result - * @return array entry for the result specification to use - */ struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_uint32 (const char *name, uint32_t *u32) @@ -952,22 +893,16 @@ extract_uint64 (void *cls, } -/** - * uint64_t expected. - * - * @param name name of the field in the table - * @param[out] u64 where to store the result - * @return array entry for the result specification to use - */ struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_uint64 (const char *name, uint64_t *u64) { - struct GNUNET_PQ_ResultSpec res = - { &extract_uint64, + struct GNUNET_PQ_ResultSpec res = { + &extract_uint64, NULL, NULL, - (void *) u64, sizeof(*u64), (name), NULL }; + (void *) u64, sizeof(*u64), (name), NULL + }; return res; } diff --git a/src/pq/test_pq.c b/src/pq/test_pq.c index b09354af8..e588da45d 100644 --- a/src/pq/test_pq.c +++ b/src/pq/test_pq.c @@ -47,10 +47,11 @@ postgres_prepare (struct GNUNET_PQ_Context *db) ",u16" ",u32" ",u64" + ",unn" ") VALUES " "($1, $2, $3, $4, $5, $6," - "$7, $8, $9);", - 9), + "$7, $8, $9, $10);", + 10), GNUNET_PQ_make_prepare ("test_select", "SELECT" " pub" @@ -62,6 +63,7 @@ postgres_prepare (struct GNUNET_PQ_Context *db) ",u16" ",u32" ",u64" + ",unn" " FROM test_pq" " ORDER BY abs_time DESC " " LIMIT 1;", @@ -106,7 +108,8 @@ run_queries (struct GNUNET_PQ_Context *db) uint32_t u322; uint64_t u64; uint64_t u642; - + uint64_t uzzz = 42; + priv = GNUNET_CRYPTO_rsa_private_key_create (1024); pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv); memset (&hmsg, 42, sizeof(hmsg)); @@ -127,11 +130,13 @@ run_queries (struct GNUNET_PQ_Context *db) GNUNET_PQ_query_param_uint16 (&u16), GNUNET_PQ_query_param_uint32 (&u32), GNUNET_PQ_query_param_uint64 (&u64), + GNUNET_PQ_query_param_null (), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_QueryParam params_select[] = { GNUNET_PQ_query_param_end }; + bool got_null = false; struct GNUNET_PQ_ResultSpec results_select[] = { GNUNET_PQ_result_spec_rsa_public_key ("pub", &pub2), GNUNET_PQ_result_spec_rsa_signature ("sig", &sig2), @@ -142,6 +147,9 @@ run_queries (struct GNUNET_PQ_Context *db) GNUNET_PQ_result_spec_uint16 ("u16", &u162), GNUNET_PQ_result_spec_uint32 ("u32", &u322), GNUNET_PQ_result_spec_uint64 ("u64", &u642), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_uint64 ("unn", &uzzz), + &got_null), GNUNET_PQ_result_spec_end }; @@ -197,6 +205,8 @@ run_queries (struct GNUNET_PQ_Context *db) GNUNET_break (16 == u162); GNUNET_break (32 == u322); GNUNET_break (64 == u642); + GNUNET_break (42 == uzzz); + GNUNET_break (got_null); GNUNET_PQ_cleanup_result (results_select); PQclear (result); } @@ -225,6 +235,7 @@ main (int argc, ",u16 INT2 NOT NULL" ",u32 INT4 NOT NULL" ",u64 INT8 NOT NULL" + ",unn INT8" ")"), GNUNET_PQ_EXECUTE_STATEMENT_END }; -- cgit v1.2.3