Move table creation logic to DB layer. Also support temporary schema creation for the sake of testing

This commit is contained in:
Sree Harsha Totakura 2015-03-05 16:05:06 +01:00
parent e6943f5a9e
commit bc55152b0a
4 changed files with 239 additions and 195 deletions

View File

@ -13,11 +13,13 @@
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file mint_db.c
* @brief Low-level (statement-level) database access for the mint
* @author Florian Dold
* @author Christian Grothoff
* @author Sree Harsha Totakura
*
* TODO:
* - The mint_db.h-API should ideally be what we need to port
@ -32,6 +34,7 @@
#include "mint_db.h"
#include <pthread.h>
/**
* Thread-local database connection.
* Contains a pointer to PGconn or NULL.
@ -45,7 +48,6 @@ static pthread_key_t db_conn_threadlocal;
*/
static char *TALER_MINT_db_connection_cfg_str;
#define break_db_err(result) do { \
GNUNET_break(0); \
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s\n", PQresultErrorMessage (result)); \
@ -60,6 +62,213 @@ static char *TALER_MINT_db_connection_cfg_str;
} while (0)
#define SQLEXEC_(conn, sql, result) \
do { \
result = PQexec (conn, sql); \
if (PGRES_COMMAND_OK != PQresultStatus (result)) \
{ \
break_db_err (result); \
PQclear (result); result = NULL; \
goto SQLEXEC_fail; \
} \
PQclear (result); result = NULL; \
} while (0)
/**
* Set the given connection to use a temporary schema
*
* @param db the database connection
* @return #GNUNET_OK upon success; #GNUNET_SYSERR upon error
*/
static int
set_temporary_schema (PGconn *db)
{
PGresult *result;
SQLEXEC_(db,
"CREATE SCHEMA IF NOT EXISTS " TALER_TEMP_SCHEMA_NAME ";"
"SET search_path to " TALER_TEMP_SCHEMA_NAME ";",
result);
return GNUNET_OK;
SQLEXEC_fail:
return GNUNET_SYSERR;
}
/**
* Drop the temporary taler schema. This is only useful for testcases
*
* @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
*/
int
TALER_MINT_DB_drop_temporary (PGconn *db)
{
PGresult *result;
SQLEXEC_ (db,
"DROP SCHEMA " TALER_TEMP_SCHEMA_NAME " CASCADE;",
result);
return GNUNET_OK;
SQLEXEC_fail:
return GNUNET_SYSERR;
}
/**
* Create the necessary tables if they are not present
*
* @param temporary should we use a temporary schema
* @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
*/
int
TALER_MINT_DB_create_tables (int temporary)
{
PGresult *result;
PGconn *conn;
result = NULL;
conn = PQconnectdb (TALER_MINT_db_connection_cfg_str);
if (CONNECTION_OK != PQstatus (conn))
{
LOG_ERROR ("Database connection failed: %s\n",
PQerrorMessage (conn));
GNUNET_break (0);
return GNUNET_SYSERR;
}
if ((GNUNET_YES == temporary)
&& (GNUNET_SYSERR == set_temporary_schema (conn)))
{
PQfinish (conn);
return GNUNET_SYSERR;
}
#define SQLEXEC(sql) SQLEXEC_(conn, sql, result);
/* reserves table is for summarization of a reserve. It is updated when new
funds are added and existing funds are withdrawn */
SQLEXEC ("CREATE TABLE IF NOT EXISTS reserves"
"("
" reserve_pub BYTEA PRIMARY KEY"
",current_balance_value INT4 NOT NULL"
",current_balance_fraction INT4 NOT NULL"
",balance_currency VARCHAR(4) NOT NULL"
",expiration_date INT8 NOT NULL"
")");
/* reserves_in table collects the transactions which transfer funds into the
reserve. The amount and expiration date for the corresponding reserve are
updated when new transfer funds are added. The rows of this table
correspond to each incoming transaction. */
SQLEXEC("CREATE TABLE IF NOT EXISTS reserves_in"
"("
" reserve_pub BYTEA REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
",balance_value INT4 NOT NULL"
",balance_fraction INT4 NOT NULL"
",expiration_date INT8 NOT NULL"
");");
result = PQexec (conn,
"CREATE INDEX reserves_in_index ON reserves_in (reserve_pub);");
if (PGRES_COMMAND_OK != PQresultStatus (result))
{
ExecStatusType status = PQresultStatus (result);
PQclear (result);
result = NULL;
goto SQLEXEC_fail;
}
PQclear (result);
SQLEXEC ("CREATE TABLE IF NOT EXISTS collectable_blindcoins"
"("
"blind_ev BYTEA PRIMARY KEY"
",blind_ev_sig BYTEA NOT NULL"
",denom_pub BYTEA NOT NULL"
",reserve_sig BYTEA NOT NULL"
",reserve_pub BYTEA REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
");"
"CREATE INDEX collectable_blindcoins_index ON"
" collectable_blindcoins(reserve_pub)");
SQLEXEC("CREATE TABLE IF NOT EXISTS known_coins "
"("
" coin_pub BYTEA NOT NULL PRIMARY KEY"
",denom_pub BYTEA NOT NULL"
",denom_sig BYTEA NOT NULL"
",expended_value INT4 NOT NULL"
",expended_fraction INT4 NOT NULL"
",expended_currency VARCHAR(4) NOT NULL"
",refresh_session_pub BYTEA"
")");
SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_sessions "
"("
" session_pub BYTEA PRIMARY KEY CHECK (length(session_pub) = 32)"
",session_melt_sig BYTEA"
",session_commit_sig BYTEA"
",noreveal_index INT2 NOT NULL"
// non-zero if all reveals were ok
// and the new coin signatures are ready
",reveal_ok BOOLEAN NOT NULL DEFAULT false"
") ");
SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_order "
"( "
" session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)"
",newcoin_index INT2 NOT NULL "
",denom_pub BYTEA NOT NULL "
",PRIMARY KEY (session_pub, newcoin_index)"
") ");
SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_link"
"("
" session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)"
",transfer_pub BYTEA NOT NULL"
",link_secret_enc BYTEA NOT NULL"
// index of the old coin in the customer's request
",oldcoin_index INT2 NOT NULL"
// index for cut and choose,
// ranges from 0 to kappa-1
",cnc_index INT2 NOT NULL"
")");
SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_coin"
"("
" session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
",link_vector_enc BYTEA NOT NULL"
// index of the new coin in the customer's request
",newcoin_index INT2 NOT NULL"
// index for cut and choose,
",cnc_index INT2 NOT NULL"
",coin_ev BYTEA NOT NULL"
")");
SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_melt"
"("
" session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) "
",denom_pub BYTEA NOT NULL "
",oldcoin_index INT2 NOT NULL"
")");
SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_collectable"
"("
" session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
",ev_sig BYTEA NOT NULL"
",newcoin_index INT2 NOT NULL"
")");
SQLEXEC("CREATE TABLE IF NOT EXISTS deposits "
"( "
" coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)"
",denom_pub BYTEA NOT NULL CHECK (length(denom_pub)=32)"
",transaction_id INT8 NOT NULL"
",amount_currency VARCHAR(4) NOT NULL"
",amount_value INT4 NOT NULL"
",amount_fraction INT4 NOT NULL"
",merchant_pub BYTEA NOT NULL"
",h_contract BYTEA NOT NULL CHECK (length(h_contract)=64)"
",h_wire BYTEA NOT NULL CHECK (length(h_wire)=64)"
",coin_sig BYTEA NOT NULL CHECK (length(coin_sig)=64)"
",wire TEXT NOT NULL"
")");
#undef SQLEXEC
PQfinish (conn);
return GNUNET_OK;
SQLEXEC_fail:
PQfinish (conn);
return GNUNET_SYSERR;
}
/**
* Setup prepared statements.
*

View File

@ -42,12 +42,32 @@ TALER_MINT_DB_init (const char *connection_cfg);
* Get the thread-local database-handle.
* Connect to the db if the connection does not exist yet.
*
* @param temporary #GNUNET_YES to use a temporary schema; #GNUNET_NO to use the
* database default one
* @param the database connection, or NULL on error
*/
PGconn *
TALER_MINT_DB_get_connection (void);
TALER_MINT_DB_get_connection (int temporary);
/**
* Drop the temporary taler schema. This is only useful for testcases
*
* @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
*/
int
TALER_MINT_DB_drop_temporary (PGconn *db);
/**
* Create the necessary tables if they are not present
*
* @param temporary should we use a temporary schema
* @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
*/
int
TALER_MINT_DB_create_tables (int temporary);
/**
* Setup prepared statements. FIXME: should this be part of the API,
* or just internal to "TALER_MINT_DB_get_connection()"?

View File

@ -36,192 +36,7 @@ static PGconn *db_conn;
static char *TALER_MINT_db_connection_cfg_str;
static int
TALER_MINT_init_withdraw_tables (PGconn *conn)
{
PGresult *result;
result = PQexec (conn,
"CREATE TABLE IF NOT EXISTS reserves"
"("
" reserve_pub BYTEA PRIMARY KEY"
",balance_value INT4 NOT NULL"
",balance_fraction INT4 NOT NULL"
",balance_currency VARCHAR(4) NOT NULL"
",status_sig BYTEA"
",status_sign_pub BYTEA"
",expiration_date INT8 NOT NULL"
")");
if (PGRES_COMMAND_OK != PQresultStatus(result))
{
break_db_err (result);
return GNUNET_SYSERR;
}
PQclear (result);
result = PQexec (conn,
"CREATE TABLE IF NOT EXISTS collectable_blindcoins"
"("
"blind_ev BYTEA PRIMARY KEY"
",blind_ev_sig BYTEA NOT NULL"
",denom_pub BYTEA NOT NULL"
",reserve_sig BYTEA NOT NULL"
",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub)"
")");
if (PGRES_COMMAND_OK != PQresultStatus(result))
{
break_db_err (result);
return GNUNET_SYSERR;
}
PQclear (result);
result = PQexec (conn,
"CREATE TABLE IF NOT EXISTS known_coins "
"("
" coin_pub BYTEA NOT NULL PRIMARY KEY"
",denom_pub BYTEA NOT NULL"
",denom_sig BYTEA NOT NULL"
",expended_value INT4 NOT NULL"
",expended_fraction INT4 NOT NULL"
",expended_currency VARCHAR(4) NOT NULL"
",refresh_session_pub BYTEA"
")");
if (PGRES_COMMAND_OK != PQresultStatus(result))
{
break_db_err (result);
return GNUNET_SYSERR;
}
PQclear (result);
result = PQexec (conn,
"CREATE TABLE IF NOT EXISTS refresh_sessions "
"("
" session_pub BYTEA PRIMARY KEY CHECK (length(session_pub) = 32)"
",session_melt_sig BYTEA"
",session_commit_sig BYTEA"
",noreveal_index INT2 NOT NULL"
// non-zero if all reveals were ok
// and the new coin signatures are ready
",reveal_ok BOOLEAN NOT NULL DEFAULT false"
") ");
if (PGRES_COMMAND_OK != PQresultStatus(result))
{
break_db_err (result);
return GNUNET_SYSERR;
}
PQclear (result);
result = PQexec (conn,
"CREATE TABLE IF NOT EXISTS refresh_order "
"( "
" session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)"
",newcoin_index INT2 NOT NULL "
",denom_pub BYTEA NOT NULL "
",PRIMARY KEY (session_pub, newcoin_index)"
") ");
if (PGRES_COMMAND_OK != PQresultStatus(result))
{
break_db_err (result);
return GNUNET_SYSERR;
}
PQclear (result);
result = PQexec (conn,
"CREATE TABLE IF NOT EXISTS refresh_commit_link"
"("
" session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)"
",transfer_pub BYTEA NOT NULL"
",link_secret_enc BYTEA NOT NULL"
// index of the old coin in the customer's request
",oldcoin_index INT2 NOT NULL"
// index for cut and choose,
// ranges from 0 to kappa-1
",cnc_index INT2 NOT NULL"
")");
if (PGRES_COMMAND_OK != PQresultStatus(result))
{
break_db_err (result);
return GNUNET_SYSERR;
}
PQclear (result);
result = PQexec (conn,
"CREATE TABLE IF NOT EXISTS refresh_commit_coin"
"("
" session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
",link_vector_enc BYTEA NOT NULL"
// index of the new coin in the customer's request
",newcoin_index INT2 NOT NULL"
// index for cut and choose,
",cnc_index INT2 NOT NULL"
",coin_ev BYTEA NOT NULL"
")");
if (PGRES_COMMAND_OK != PQresultStatus(result))
{
break_db_err (result);
return GNUNET_SYSERR;
}
PQclear (result);
result = PQexec (conn,
"CREATE TABLE IF NOT EXISTS refresh_melt"
"("
" session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) "
",denom_pub BYTEA NOT NULL "
",oldcoin_index INT2 NOT NULL"
")");
if (PGRES_COMMAND_OK != PQresultStatus(result))
{
break_db_err (result);
return GNUNET_SYSERR;
}
PQclear (result);
result = PQexec (conn,
"CREATE TABLE IF NOT EXISTS refresh_collectable"
"("
" session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
",ev_sig BYTEA NOT NULL"
",newcoin_index INT2 NOT NULL"
")");
if (PGRES_COMMAND_OK != PQresultStatus(result))
{
break_db_err (result);
return GNUNET_SYSERR;
}
PQclear (result);
result = PQexec (conn,
"CREATE TABLE IF NOT EXISTS deposits "
"( "
" coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)"
",denom_pub BYTEA NOT NULL CHECK (length(denom_pub)=32)"
",transaction_id INT8 NOT NULL"
",amount_currency VARCHAR(4) NOT NULL"
",amount_value INT4 NOT NULL"
",amount_fraction INT4 NOT NULL"
",merchant_pub BYTEA NOT NULL"
",h_contract BYTEA NOT NULL CHECK (length(h_contract)=64)"
",h_wire BYTEA NOT NULL CHECK (length(h_wire)=64)"
",coin_sig BYTEA NOT NULL CHECK (length(coin_sig)=64)"
",wire TEXT NOT NULL"
")");
if (PGRES_COMMAND_OK != PQresultStatus(result))
{
break_db_err (result);
return GNUNET_SYSERR;
}
PQclear (result);
return GNUNET_OK;
}
/**
@ -271,7 +86,7 @@ main (int argc, char *const *argv)
return 1;
}
if (GNUNET_OK != TALER_MINT_init_withdraw_tables (db_conn))
if (GNUNET_OK != TALER_MINT_DB_create_tables (db_conn))
{
fprintf (stderr, "Failed to initialize database.\n");
return 1;

View File

@ -85,7 +85,7 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,
struct TALER_MINT_DenomKeyIssuePriv *dki;
int ret;
if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO)))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
@ -203,7 +203,7 @@ TALER_MINT_db_execute_withdraw_status (struct MHD_Connection *connection,
struct ReserveHistory *rh;
int res;
if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO)))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
@ -264,7 +264,7 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
blinded_msg_len,
&h_blind);
if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO)))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
@ -521,7 +521,7 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
int res;
unsigned int i;
if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO)))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
@ -650,7 +650,7 @@ TALER_MINT_db_execute_refresh_commit (struct MHD_Connection *connection,
unsigned int j;
int res;
if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO)))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
@ -1034,7 +1034,7 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,
unsigned int j;
unsigned int off;
if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO)))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
@ -1202,7 +1202,7 @@ TALER_MINT_db_execute_refresh_link (struct MHD_Connection *connection,
struct TALER_EncryptedLinkSecret shared_secret_enc;
struct LinkDataList *ldl;
if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO)))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);