/*
This file is part of GNUnet
(C) 2009, 2010 Christian Grothoff (and other contributing authors)
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 3, or (at your
option) any later version.
GNUnet is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNUnet; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
/**
* @file datastore/plugin_datastore_postgres.c
* @brief postgres-based datastore backend
* @author Christian Grothoff
*/
#include "platform.h"
#include "plugin_datastore.h"
#include <postgresql/libpq-fe.h>
#define DEBUG_POSTGRES GNUNET_NO
#define SELECT_IT_LOW_PRIORITY "(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
"WHERE (prio = $1 AND oid > $2) " \
"ORDER BY prio ASC,oid ASC LIMIT 1) "\
"UNION "\
"(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
"WHERE (prio > $1 AND oid != $2)"\
"ORDER BY prio ASC,oid ASC LIMIT 1)"\
"ORDER BY prio ASC,oid ASC LIMIT 1"
#define SELECT_IT_NON_ANONYMOUS "(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
"WHERE (prio = $1 AND oid < $2)"\
" AND anonLevel=0 ORDER BY prio DESC,oid DESC LIMIT 1) "\
"UNION "\
"(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
"WHERE (prio < $1 AND oid != $2)"\
" AND anonLevel=0 ORDER BY prio DESC,oid DESC LIMIT 1) "\
"ORDER BY prio DESC,oid DESC LIMIT 1"
#define SELECT_IT_EXPIRATION_TIME "(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
"WHERE (expire = $1 AND oid > $2) "\
"ORDER BY expire ASC,oid ASC LIMIT 1) "\
"UNION "\
"(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
"WHERE (expire > $1 AND oid != $2) " \
"ORDER BY expire ASC,oid ASC LIMIT 1)"\
"ORDER BY expire ASC,oid ASC LIMIT 1"
#define SELECT_IT_MIGRATION_ORDER "(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
"WHERE (expire = $1 AND oid < $2)"\
" AND expire > $3 AND type!=3"\
" ORDER BY expire DESC,oid DESC LIMIT 1) "\
"UNION "\
"(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
"WHERE (expire < $1 AND oid != $2)" \
" AND expire > $3 AND type!=3"\
" ORDER BY expire DESC,oid DESC LIMIT 1)"\
"ORDER BY expire DESC,oid DESC LIMIT 1"
/**
* After how many ms "busy" should a DB operation fail for good?
* A low value makes sure that we are more responsive to requests
* (especially PUTs). A high value guarantees a higher success
* rate (SELECTs in iterate can take several seconds despite LIMIT=1).
*
* The default value of 1s should ensure that users do not experience
* huge latencies while at the same time allowing operations to succeed
* with reasonable probability.
*/
#define BUSY_TIMEOUT GNUNET_TIME_UNIT_SECONDS
struct NextRequestClosure
{
struct Plugin *plugin;
PluginIterator iter;
void *iter_cls;
const char *paramValues[5];
const char *pname;
int paramLengths[5];
int nparams; // nparams
struct GNUNET_TIME_Absolute now;
GNUNET_HashCode key;
GNUNET_HashCode vhash;
long long count;
long long off;
long long limit_off;
unsigned long long total;
unsigned long long last_expire;
unsigned int last_rowid; // last_rowid
unsigned int last_prio;
enum GNUNET_BLOCK_Type type;
int end_it;
};
/**
* Context for all functions in this plugin.
*/
struct Plugin
{
/**
* Our execution environment.
*/
struct GNUNET_DATASTORE_PluginEnvironment *env;
/**
* Native Postgres database handle.
*/
PGconn *dbh;
/**
* Closure of the 'next_task' (must be freed if 'next_task' is cancelled).
*/
struct NextRequestClosure *next_task_nc;
/**
* Pending task with scheduler for running the next request.
*/
GNUNET_SCHEDULER_TaskIdentifier next_task;
unsigned long long payload;
unsigned int lastSync;
};
/**
* Check if the result obtained from Postgres has
* the desired status code. If not, log an error, clear the
* result and return GNUNET_SYSERR.
*
* @return GNUNET_OK if the result is acceptable
*/
static int
check_result (struct Plugin *plugin,
PGresult * ret,
int expected_status,
const char *command, const char *args, int line)
{
if (ret == NULL)
{
GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
"datastore-postgres",
"Postgres failed to allocate result for `%s:%s' at %d\n",
command, args, line);
return GNUNET_SYSERR;
}
if (PQresultStatus (ret) != expected_status)
{
GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR