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:
parent
4af6cbb375
commit
85e59ceb1a
@ -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 */
|
||||||
|
235
src/pq/db_pq.c
235
src/pq/db_pq.c
@ -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 = ¶ms[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 = ¶ms[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;
|
||||||
|
Loading…
Reference in New Issue
Block a user