expanding PQ APIs to be able to handle Amounts nicely (and be extensible for additional data types in the future)

This commit is contained in:
Christian Grothoff 2015-05-06 11:13:09 +02:00
parent 4af6cbb375
commit 85e59ceb1a
2 changed files with 220 additions and 80 deletions

View File

@ -26,11 +26,47 @@
#include <libpq-fe.h> #include <libpq-fe.h>
#include "taler_util.h" #include "taler_util.h"
/**
* Different formats of results that can be added to a query.
*/
enum TALER_PQ_QueryFormat
{
/**
* List terminator.
*/
TALER_PQ_QF_END,
/**
* We have a fixed-size result (binary blob, no endianess conversion).
*/
TALER_PQ_QF_FIXED_BLOB,
/**
* We have a variable-size result (binary blob, no endianess conversion).
*/
TALER_PQ_QF_VARSIZE_BLOB,
/**
* We have a currency amount (with endianess conversion).
* Data points to a `struct TALER_AmountNBO`, size is not used.
*/
TALER_PQ_QF_AMOUNT_NBO
};
/** /**
* @brief Description of a DB query parameter. * @brief Description of a DB query parameter.
*/ */
struct TALER_PQ_QueryParam struct TALER_PQ_QueryParam
{ {
/**
* Format of the rest of the entry, determines the data
* type that is being added to the query.
*/
enum TALER_PQ_QueryFormat format;
/** /**
* Data or NULL. * Data or NULL.
*/ */
@ -41,18 +77,13 @@ struct TALER_PQ_QueryParam
*/ */
size_t size; size_t size;
/**
* Non-null if this is not the last parameter.
* This allows us to detect the end of the list.
*/
int more;
}; };
/** /**
* End of query parameter specification. * End of query parameter specification.
*/ */
#define TALER_PQ_QUERY_PARAM_END { NULL, 0, 0 } #define TALER_PQ_QUERY_PARAM_END { TALER_PQ_QF_END, NULL, 0 }
/** /**
* Generate fixed-size query parameter with size given explicitly. * Generate fixed-size query parameter with size given explicitly.
@ -60,7 +91,7 @@ struct TALER_PQ_QueryParam
* @param x pointer to the query parameter to pass * @param x pointer to the query parameter to pass
* @param s number of bytes of @a x to use for the query * @param s number of bytes of @a x to use for the query
*/ */
#define TALER_PQ_QUERY_PARAM_PTR_SIZED(x, s) { (x), (s), 1 } #define TALER_PQ_QUERY_PARAM_PTR_SIZED(x, s) { TALER_PQ_QF_FIXED_BLOB, (x), (s) }
/** /**
* Generate fixed-size query parameter with size determined * Generate fixed-size query parameter with size determined
@ -68,7 +99,15 @@ struct TALER_PQ_QueryParam
* *
* @param x pointer to the query parameter to pass. * @param x pointer to the query parameter to pass.
*/ */
#define TALER_PQ_QUERY_PARAM_PTR(x) TALER_PQ_QUERY_PARAM_PTR_SIZED(x, sizeof (*(x))) #define TALER_PQ_QUERY_PARAM_PTR(x) { TALER_PQ_QF_VARSIZE_BLOB, x, sizeof (*(x)) }
/**
* Generate fixed-size query parameter with size determined
* by variable type.
*
* @param x pointer to the query parameter to pass.
*/
#define TALER_PQ_QUERY_PARAM_AMOUNT_NBO(x) { TALER_PQ_QF_AMOUNT_NBO, x, sizeof (*(x)) }
/** /**
@ -93,10 +132,10 @@ enum TALER_PQ_ResultFormat
TALER_PQ_RF_VARSIZE_BLOB, TALER_PQ_RF_VARSIZE_BLOB,
/** /**
* We have a currency amount (with endianess conversion). * We have a currency amount.
* Data points to a `struct TALER_Amount`, size is not used. * Data points to a `struct TALER_AmountNBO`, size is not used.
*/ */
TALER_PQ_RF_AMOUNT TALER_PQ_RF_AMOUNT_NBO
}; };
@ -176,7 +215,7 @@ struct TALER_PQ_ResultSpec
* @param name name of the field in the table * @param name name of the field in the table
* @param amount a `struct TALER_Amount` where to store the result * @param amount a `struct TALER_Amount` where to store the result
*/ */
#define TALER_PQ_RESULT_SPEC_AMOUNT(name, amount) {TALER_PQ_RF_AMOUNT, (void *) (&dst), 0, (name), sptr } #define TALER_PQ_RESULT_SPEC_AMOUNT(name, amount) {TALER_PQ_RF_AMOUNT, (void *) (&dst), sizeof (amount), (name), NULL }
/** /**
@ -264,4 +303,4 @@ TALER_PQ_extract_amount (PGresult *result,
#endif /* TALER_PQ_LIB_H_ */ #endif /* TALER_PQ_LIB_H_ */
/* end of db/taler_pq_lib.h */ /* end of include/taler_pq_lib.h */

View File

@ -27,6 +27,11 @@
/** /**
* Execute a prepared statement. * Execute a prepared statement.
*
* @param db_conn database connection
* @param name name of the prepared statement
* @param params parameters to the statement
* @return postgres result
*/ */
PGresult * PGresult *
TALER_PQ_exec_prepared (PGconn *db_conn, TALER_PQ_exec_prepared (PGconn *db_conn,
@ -37,11 +42,26 @@ TALER_PQ_exec_prepared (PGconn *db_conn,
unsigned int i; unsigned int i;
/* count the number of parameters */ /* count the number of parameters */
i = 0;
len = 0;
while (TALER_PQ_QF_END != params[i].format)
{ {
const struct TALER_PQ_QueryParam *x; const struct TALER_PQ_QueryParam *x = &params[i];
for (len = 0, x = params;
x->more; switch (x->format)
len++, x++); {
case TALER_PQ_RF_FIXED_BLOB:
case TALER_PQ_RF_VARSIZE_BLOB:
len++;
break;
case TALER_PQ_RF_AMOUNT_NBO:
len += 3;
break;
default:
/* format not supported */
GNUNET_assert (0);
break;
}
} }
/* new scope to allow stack allocation without alloca */ /* new scope to allow stack allocation without alloca */
@ -49,13 +69,48 @@ TALER_PQ_exec_prepared (PGconn *db_conn,
void *param_values[len]; void *param_values[len];
int param_lengths[len]; int param_lengths[len];
int param_formats[len]; int param_formats[len];
unsigned int off;
for (i = 0; i < len; i += 1) i = 0;
off = 0;
while (TALER_PQ_QF_END != params[i].format)
{ {
param_values[i] = (void *) params[i].data; const struct TALER_PQ_QueryParam *x = &params[i];
param_lengths[i] = params[i].size;
param_formats[i] = 1; switch (x->format)
{
case TALER_PQ_RF_FIXED_BLOB:
case TALER_PQ_RF_VARSIZE_BLOB:
param_values[off] = (void *) x->data;
param_lengths[off] = x->size;
param_formats[off] = 1;
off++;
break;
case TALER_PQ_RF_AMOUNT_NBO:
{
const struct TALER_Amount *amount = x->data;
param_values[off] = (void *) &amount->value;
param_lengths[off] = sizeof (amount->value);
param_formats[off] = 1;
off++;
param_values[off] = (void *) &amount->fraction;
param_lengths[off] = sizeof (amount->fraction);
param_formats[off] = 1;
off++;
param_values[off] = (void *) amount->currency;
param_lengths[off] = strlen (amount->currency) + 1;
param_formats[off] = 1;
off++;
}
break;
default:
/* format not supported */
GNUNET_assert (0);
break;
}
} }
GNUNET_assert (off == len);
return PQexecPrepared (db_conn, return PQexecPrepared (db_conn,
name, name,
len, len,
@ -85,75 +140,121 @@ TALER_PQ_extract_result (PGresult *result,
struct TALER_PQ_ResultSpec *rs, struct TALER_PQ_ResultSpec *rs,
int row) int row)
{ {
int had_null = GNUNET_NO;
size_t len;
unsigned int i; unsigned int i;
unsigned int j; int had_null = GNUNET_NO;
const char *res;
void *dst;
int fnum;
for (i=0; NULL != rs[i].fname; i++) for (i=0; TALER_PQ_RF_END != rs[i].format; i++)
{ {
fnum = PQfnumber (result, struct TALER_PQ_ResultSpec *spec;
rs[i].fname);
if (fnum < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Field `%s' does not exist in result\n",
rs[i].fname);
return GNUNET_SYSERR;
}
/* if a field is null, continue but spec = &rs[i];
* remember that we now return a different result */ switch (spec->format)
if (PQgetisnull (result,
row,
fnum))
{ {
had_null = GNUNET_YES; case TALER_PQ_RF_FIXED_BLOB:
continue; case TALER_PQ_RF_VARSIZE_BLOB:
}
len = PQgetlength (result,
row,
fnum);
if ( (0 != rs[i].dst_size) &&
(rs[i].dst_size != len) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Field `%s' has wrong size (got %u, expected %u)\n",
rs[i].fname,
(unsigned int) len,
(unsigned int) rs[i].dst_size);
for (j=0; j<i; j++)
{ {
if (0 == rs[j].dst_size) size_t len;
unsigned int j;
const char *res;
void *dst;
int fnum;
fnum = PQfnumber (result,
spec->fname);
if (fnum < 0)
{ {
GNUNET_free (rs[j].dst); GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
rs[j].dst = NULL; "Field `%s' does not exist in result\n",
if (NULL != rs[j].result_size) spec->fname);
*rs[j].result_size = 0; return GNUNET_SYSERR;
} }
if (PQgetisnull (result,
row,
fnum))
{
had_null = GNUNET_YES;
continue;
}
/* if a field is null, continue but
* remember that we now return a different result */
len = PQgetlength (result,
row,
fnum);
if ( (0 != rs[i].dst_size) &&
(rs[i].dst_size != len) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Field `%s' has wrong size (got %u, expected %u)\n",
rs[i].fname,
(unsigned int) len,
(unsigned int) rs[i].dst_size);
for (j=0; j<i; j++)
{
if (0 == rs[j].dst_size)
{
GNUNET_free (rs[j].dst);
rs[j].dst = NULL;
if (NULL != rs[j].result_size)
*rs[j].result_size = 0;
}
}
return GNUNET_SYSERR;
}
res = PQgetvalue (result,
row,
fnum);
GNUNET_assert (NULL != res);
if (0 == rs[i].dst_size)
{
if (NULL != rs[i].result_size)
*rs[i].result_size = len;
rs[i].dst_size = len;
dst = GNUNET_malloc (len);
*((void **) rs[i].dst) = dst;
}
else
dst = rs[i].dst;
memcpy (dst,
res,
len);
break;
} }
return GNUNET_SYSERR; case TALER_PQ_RF_AMOUNT_NBO:
{
char *val_name;
char *frac_name;
char *curr_name;
const char *name = spec->fname;
GNUNET_assert (NULL != rs[i].dst);
GNUNET_asprintf (&val_name,
"%s_val",
name);
GNUNET_asprintf (&frac_name,
"%s_frac",
name);
GNUNET_asprintf (&curr_name,
"%s_curr",
name);
if (GNUNET_YES !=
TALER_PQ_extract_amount_nbo (result,
row,
val_name,
frac_name,
curr_name,
rs[i].dst))
had_null = GNUNET_YES;
GNUNET_free (val_name);
GNUNET_free (frac_name);
GNUNET_free (curr_name);
break;
}
default:
GNUNET_assert (0);
break;
} }
res = PQgetvalue (result,
row,
fnum);
GNUNET_assert (NULL != res);
if (0 == rs[i].dst_size)
{
if (NULL != rs[i].result_size)
*rs[i].result_size = len;
rs[i].dst_size = len;
dst = GNUNET_malloc (len);
*((void **) rs[i].dst) = dst;
}
else
dst = rs[i].dst;
memcpy (dst,
res,
len);
} }
if (GNUNET_YES == had_null) if (GNUNET_YES == had_null)
return GNUNET_NO; return GNUNET_NO;