2 This file is part of GNUnet
3 Copyright (C) 2009-2016 GNUnet e.V.
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 * @file datastore/plugin_datastore_postgres.c
23 * @brief postgres-based datastore backend
24 * @author Christian Grothoff
28 #include "gnunet_datastore_plugin.h"
29 #include "gnunet_postgres_lib.h"
30 #include "gnunet_pq_lib.h"
34 * After how many ms "busy" should a DB operation fail for good?
35 * A low value makes sure that we are more responsive to requests
36 * (especially PUTs). A high value guarantees a higher success
37 * rate (SELECTs in iterate can take several seconds despite LIMIT=1).
39 * The default value of 1s should ensure that users do not experience
40 * huge latencies while at the same time allowing operations to succeed
41 * with reasonable probability.
43 #define BUSY_TIMEOUT GNUNET_TIME_UNIT_SECONDS
47 * Context for all functions in this plugin.
52 * Our execution environment.
54 struct GNUNET_DATASTORE_PluginEnvironment *env;
57 * Native Postgres database handle.
65 * @brief Get a database handle
67 * @param plugin global context
68 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
71 init_connection (struct Plugin *plugin)
75 plugin->dbh = GNUNET_POSTGRES_connect (plugin->env->cfg, "datastore-postgres");
76 if (NULL == plugin->dbh)
79 /* FIXME: PostgreSQL does not have unsigned integers! This is ok for the type column because
80 * we only test equality on it and can cast it to/from uint32_t. For repl, prio, and anonLevel
81 * we do math or inequality tests, so we can't handle the entire range of uint32_t.
82 * This will also cause problems for expiration times after 294247-01-10-04:00:54 UTC.
86 "CREATE TABLE IF NOT EXISTS gn090 ("
87 " repl INTEGER NOT NULL DEFAULT 0,"
88 " type INTEGER NOT NULL DEFAULT 0,"
89 " prio INTEGER NOT NULL DEFAULT 0,"
90 " anonLevel INTEGER NOT NULL DEFAULT 0,"
91 " expire BIGINT NOT NULL DEFAULT 0,"
92 " rvalue BIGINT NOT NULL DEFAULT 0,"
93 " hash BYTEA NOT NULL DEFAULT '',"
94 " vhash BYTEA NOT NULL DEFAULT '',"
95 " value BYTEA NOT NULL DEFAULT '')"
98 ((PQresultStatus (ret) != PGRES_COMMAND_OK) &&
99 (0 != strcmp ("42P07", /* duplicate table */
102 PG_DIAG_SQLSTATE)))))
104 (void) GNUNET_POSTGRES_check_result (plugin->dbh,
109 PQfinish (plugin->dbh);
111 return GNUNET_SYSERR;
114 if (PQresultStatus (ret) == PGRES_COMMAND_OK)
117 GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX IF NOT EXISTS idx_hash ON gn090 (hash)")) ||
119 GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX IF NOT EXISTS idx_hash_vhash ON gn090 (hash,vhash)")) ||
121 GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX IF NOT EXISTS idx_prio ON gn090 (prio)")) ||
123 GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX IF NOT EXISTS idx_expire ON gn090 (expire)")) ||
125 GNUNET_POSTGRES_exec (plugin->dbh,
126 "CREATE INDEX IF NOT EXISTS idx_prio_anon ON gn090 (prio,anonLevel)")) ||
128 GNUNET_POSTGRES_exec (plugin->dbh,
129 "CREATE INDEX IF NOT EXISTS idx_prio_hash_anon ON gn090 (prio,hash,anonLevel)")) ||
131 GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX IF NOT EXISTS idx_repl_rvalue ON gn090 (repl,rvalue)")) ||
133 GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX IF NOT EXISTS idx_expire_hash ON gn090 (expire,hash)")))
136 PQfinish (plugin->dbh);
138 return GNUNET_SYSERR;
145 "ALTER TABLE gn090 ALTER value SET STORAGE EXTERNAL");
147 GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn090"))
149 PQfinish (plugin->dbh);
151 return GNUNET_SYSERR;
154 ret = PQexec (plugin->dbh, "ALTER TABLE gn090 ALTER hash SET STORAGE PLAIN");
156 GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn090"))
158 PQfinish (plugin->dbh);
160 return GNUNET_SYSERR;
163 ret = PQexec (plugin->dbh, "ALTER TABLE gn090 ALTER vhash SET STORAGE PLAIN");
165 GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn090"))
167 PQfinish (plugin->dbh);
169 return GNUNET_SYSERR;
173 GNUNET_POSTGRES_prepare (plugin->dbh, "getvt",
174 "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
175 "WHERE hash=$1 AND vhash=$2 AND type=$3 "
176 "ORDER BY oid ASC LIMIT 1 OFFSET $4", 4)) ||
178 GNUNET_POSTGRES_prepare (plugin->dbh, "gett",
179 "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
180 "WHERE hash=$1 AND type=$2 "
181 "ORDER BY oid ASC LIMIT 1 OFFSET $3", 3)) ||
183 GNUNET_POSTGRES_prepare (plugin->dbh, "getv",
184 "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
185 "WHERE hash=$1 AND vhash=$2 "
186 "ORDER BY oid ASC LIMIT 1 OFFSET $3", 3)) ||
188 GNUNET_POSTGRES_prepare (plugin->dbh, "get",
189 "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
190 "WHERE hash=$1 " "ORDER BY oid ASC LIMIT 1 OFFSET $2", 2)) ||
192 GNUNET_POSTGRES_prepare (plugin->dbh, "count_getvt",
193 "SELECT count(*) FROM gn090 WHERE hash=$1 AND vhash=$2 AND type=$3", 3)) ||
195 GNUNET_POSTGRES_prepare (plugin->dbh, "count_gett",
196 "SELECT count(*) FROM gn090 WHERE hash=$1 AND type=$2", 2)) ||
198 GNUNET_POSTGRES_prepare (plugin->dbh, "count_getv",
199 "SELECT count(*) FROM gn090 WHERE hash=$1 AND vhash=$2", 2)) ||
201 GNUNET_POSTGRES_prepare (plugin->dbh, "count_get",
202 "SELECT count(*) FROM gn090 WHERE hash=$1", 1)) ||
204 GNUNET_POSTGRES_prepare (plugin->dbh, "put",
205 "INSERT INTO gn090 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) "
206 "VALUES ($1, $2, $3, $4, $5, RANDOM(), $6, $7, $8)", 9)) ||
208 GNUNET_POSTGRES_prepare (plugin->dbh, "update",
209 "UPDATE gn090 SET prio = prio + $1, expire = CASE WHEN expire < $2 THEN $2 ELSE expire END "
210 "WHERE oid = $3", 3)) ||
212 GNUNET_POSTGRES_prepare (plugin->dbh, "decrepl",
213 "UPDATE gn090 SET repl = GREATEST (repl - 1, 0) "
214 "WHERE oid = $1", 1)) ||
216 GNUNET_POSTGRES_prepare (plugin->dbh, "select_non_anonymous",
217 "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
218 "WHERE anonLevel = 0 AND type = $1 ORDER BY oid DESC LIMIT 1 OFFSET $2",
221 GNUNET_POSTGRES_prepare (plugin->dbh, "select_expiration_order",
222 "(SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
223 "WHERE expire < $1 ORDER BY prio ASC LIMIT 1) " "UNION "
224 "(SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
225 "ORDER BY prio ASC LIMIT 1) " "ORDER BY expire ASC LIMIT 1",
228 GNUNET_POSTGRES_prepare (plugin->dbh, "select_replication_order",
229 "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
230 "ORDER BY repl DESC,RANDOM() LIMIT 1", 0)) ||
232 GNUNET_POSTGRES_prepare (plugin->dbh, "delrow", "DELETE FROM gn090 " "WHERE oid=$1", 1)) ||
234 GNUNET_POSTGRES_prepare (plugin->dbh, "get_keys", "SELECT hash FROM gn090", 0)))
236 PQfinish (plugin->dbh);
238 return GNUNET_SYSERR;
245 * Get an estimate of how much space the database is
248 * @param cls our `struct Plugin *`
249 * @return number of bytes used on disk
252 postgres_plugin_estimate_size (void *cls, unsigned long long *estimate)
254 struct Plugin *plugin = cls;
255 unsigned long long total;
258 if (NULL == estimate)
261 PQexecParams (plugin->dbh,
262 "SELECT SUM(LENGTH(value))+256*COUNT(*) FROM gn090", 0,
263 NULL, NULL, NULL, NULL, 1);
265 GNUNET_POSTGRES_check_result (plugin->dbh,
274 if ((PQntuples (ret) != 1) || (PQnfields (ret) != 1) )
281 if (PQgetlength (ret, 0, 0) != sizeof (unsigned long long))
283 GNUNET_break (0 == PQgetlength (ret, 0, 0));
288 total = GNUNET_ntohll (*(const unsigned long long *) PQgetvalue (ret, 0, 0));
295 * Store an item in the datastore.
297 * @param cls closure with the `struct Plugin`
298 * @param key key for the item
299 * @param size number of bytes in data
300 * @param data content stored
301 * @param type type of the content
302 * @param priority priority of the content
303 * @param anonymity anonymity-level for the content
304 * @param replication replication-level for the content
305 * @param expiration expiration time for the content
306 * @param cont continuation called with success or failure status
307 * @param cont_cls continuation closure
310 postgres_plugin_put (void *cls,
311 const struct GNUNET_HashCode *key,
314 enum GNUNET_BLOCK_Type type,
317 uint32_t replication,
318 struct GNUNET_TIME_Absolute expiration,
322 struct Plugin *plugin = cls;
323 uint32_t utype = type;
324 struct GNUNET_HashCode vhash;
326 struct GNUNET_PQ_QueryParam params[] = {
327 GNUNET_PQ_query_param_uint32 (&replication),
328 GNUNET_PQ_query_param_uint32 (&utype),
329 GNUNET_PQ_query_param_uint32 (&priority),
330 GNUNET_PQ_query_param_uint32 (&anonymity),
331 GNUNET_PQ_query_param_absolute_time (&expiration),
332 GNUNET_PQ_query_param_auto_from_type (key),
333 GNUNET_PQ_query_param_auto_from_type (&vhash),
334 GNUNET_PQ_query_param_fixed_size (data, size),
335 GNUNET_PQ_query_param_end
338 GNUNET_CRYPTO_hash (data, size, &vhash);
339 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
343 GNUNET_POSTGRES_check_result (plugin->dbh,
346 "PQexecPrepared", "put"))
348 cont (cont_cls, key, size,
350 _("Postgress exec failure"));
354 plugin->env->duc (plugin->env->cls,
355 size + GNUNET_DATASTORE_ENTRY_OVERHEAD);
356 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
357 "datastore-postgres",
358 "Stored %u bytes in database\n",
359 (unsigned int) size);
360 cont (cont_cls, key, size, GNUNET_OK, NULL);
365 * Function invoked to process the result and call the processor.
367 * @param plugin global plugin data
368 * @param proc function to call the value (once only).
369 * @param proc_cls closure for proc
370 * @param res result from exec
371 * @param filename filename for error messages
372 * @param line line number for error messages
375 process_result (struct Plugin *plugin,
376 PluginDatumProcessor proc,
379 const char *filename, int line)
388 struct GNUNET_TIME_Absolute expiration_time;
389 struct GNUNET_HashCode key;
390 struct GNUNET_PQ_ResultSpec rs[] = {
391 GNUNET_PQ_result_spec_uint32 ("type", &utype),
392 GNUNET_PQ_result_spec_uint32 ("prio", &priority),
393 GNUNET_PQ_result_spec_uint32 ("anonLevel", &anonymity),
394 GNUNET_PQ_result_spec_uint32 ("oid", &rowid),
395 GNUNET_PQ_result_spec_absolute_time ("expire", &expiration_time),
396 GNUNET_PQ_result_spec_auto_from_type ("hash", &key),
397 GNUNET_PQ_result_spec_variable_size ("value", &data, &size),
398 GNUNET_PQ_result_spec_end
402 GNUNET_POSTGRES_check_result_ (plugin->dbh,
409 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
410 "datastore-postgres",
411 "Ending iteration (postgres error)\n");
412 proc (proc_cls, NULL, 0, NULL, 0, 0, 0,
413 GNUNET_TIME_UNIT_ZERO_ABS, 0);
417 if (0 == PQntuples (res))
420 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
421 "datastore-postgres",
422 "Ending iteration (no more results)\n");
423 proc (proc_cls, NULL, 0, NULL, 0, 0, 0,
424 GNUNET_TIME_UNIT_ZERO_ABS, 0);
428 if (1 != PQntuples (res))
431 proc (proc_cls, NULL, 0, NULL, 0, 0, 0,
432 GNUNET_TIME_UNIT_ZERO_ABS, 0);
437 GNUNET_PQ_extract_result (res,
443 GNUNET_POSTGRES_delete_by_rowid (plugin->dbh,
446 proc (proc_cls, NULL, 0, NULL, 0, 0, 0,
447 GNUNET_TIME_UNIT_ZERO_ABS, 0);
451 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
452 "datastore-postgres",
453 "Found result of size %u bytes and type %u in database\n",
455 (unsigned int) utype);
456 iret = proc (proc_cls,
460 (enum GNUNET_BLOCK_Type) utype,
466 if (iret == GNUNET_NO)
468 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
469 "Processor asked for item %u to be removed.\n",
470 (unsigned int) rowid);
472 GNUNET_POSTGRES_delete_by_rowid (plugin->dbh,
476 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
477 "datastore-postgres",
478 "Deleting %u bytes from database\n",
479 (unsigned int) size);
480 plugin->env->duc (plugin->env->cls,
481 - (size + GNUNET_DATASTORE_ENTRY_OVERHEAD));
482 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
483 "datastore-postgres",
484 "Deleted %u bytes from database\n",
485 (unsigned int) size);
492 * Iterate over the results for a particular key
495 * @param cls closure with the 'struct Plugin'
496 * @param offset offset of the result (modulo num-results);
497 * specific ordering does not matter for the offset
498 * @param key maybe NULL (to match all entries)
499 * @param vhash hash of the value, maybe NULL (to
500 * match all values that have the right key).
501 * Note that for DBlocks there is no difference
502 * betwen key and vhash, but for other blocks
504 * @param type entries of which type are relevant?
505 * Use 0 for any type.
506 * @param proc function to call on the matching value;
507 * will be called once with a NULL if no value matches
508 * @param proc_cls closure for iter
511 postgres_plugin_get_key (void *cls,
513 const struct GNUNET_HashCode *key,
514 const struct GNUNET_HashCode *vhash,
515 enum GNUNET_BLOCK_Type type,
516 PluginDatumProcessor proc,
519 struct Plugin *plugin = cls;
520 uint32_t utype = type;
529 struct GNUNET_PQ_QueryParam params[] = {
530 GNUNET_PQ_query_param_auto_from_type (key),
531 GNUNET_PQ_query_param_auto_from_type (vhash),
532 GNUNET_PQ_query_param_uint32 (&utype),
533 GNUNET_PQ_query_param_end
535 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
541 struct GNUNET_PQ_QueryParam params[] = {
542 GNUNET_PQ_query_param_auto_from_type (key),
543 GNUNET_PQ_query_param_uint32 (&utype),
544 GNUNET_PQ_query_param_end
546 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
555 struct GNUNET_PQ_QueryParam params[] = {
556 GNUNET_PQ_query_param_auto_from_type (key),
557 GNUNET_PQ_query_param_auto_from_type (vhash),
558 GNUNET_PQ_query_param_end
560 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
566 struct GNUNET_PQ_QueryParam params[] = {
567 GNUNET_PQ_query_param_auto_from_type (key),
568 GNUNET_PQ_query_param_end
570 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
577 GNUNET_POSTGRES_check_result (plugin->dbh,
583 proc (proc_cls, NULL, 0, NULL, 0, 0, 0,
584 GNUNET_TIME_UNIT_ZERO_ABS, 0);
587 if ( (PQntuples (ret) != 1) ||
588 (PQnfields (ret) != 1) ||
589 (PQgetlength (ret, 0, 0) != sizeof (uint64_t)))
593 proc (proc_cls, NULL, 0, NULL, 0, 0, 0,
594 GNUNET_TIME_UNIT_ZERO_ABS, 0);
597 total = GNUNET_ntohll (*(const uint64_t *) PQgetvalue (ret, 0, 0));
601 proc (proc_cls, NULL, 0, NULL, 0, 0, 0,
602 GNUNET_TIME_UNIT_ZERO_ABS, 0);
605 limit_off = offset % total;
611 struct GNUNET_PQ_QueryParam params[] = {
612 GNUNET_PQ_query_param_auto_from_type (key),
613 GNUNET_PQ_query_param_auto_from_type (vhash),
614 GNUNET_PQ_query_param_uint32 (&utype),
615 GNUNET_PQ_query_param_uint64 (&limit_off),
616 GNUNET_PQ_query_param_end
618 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
624 struct GNUNET_PQ_QueryParam params[] = {
625 GNUNET_PQ_query_param_auto_from_type (key),
626 GNUNET_PQ_query_param_uint32 (&utype),
627 GNUNET_PQ_query_param_uint64 (&limit_off),
628 GNUNET_PQ_query_param_end
630 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
639 struct GNUNET_PQ_QueryParam params[] = {
640 GNUNET_PQ_query_param_auto_from_type (key),
641 GNUNET_PQ_query_param_auto_from_type (vhash),
642 GNUNET_PQ_query_param_uint64 (&limit_off),
643 GNUNET_PQ_query_param_end
645 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
651 struct GNUNET_PQ_QueryParam params[] = {
652 GNUNET_PQ_query_param_auto_from_type (key),
653 GNUNET_PQ_query_param_uint64 (&limit_off),
654 GNUNET_PQ_query_param_end
656 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
661 process_result (plugin,
670 * Select a subset of the items in the datastore and call
671 * the given iterator for each of them.
673 * @param cls our `struct Plugin *`
674 * @param offset offset of the result (modulo num-results);
675 * specific ordering does not matter for the offset
676 * @param type entries of which type should be considered?
677 * Use 0 for any type.
678 * @param proc function to call on the matching value;
679 * will be called with a NULL if no value matches
680 * @param proc_cls closure for @a proc
683 postgres_plugin_get_zero_anonymity (void *cls,
685 enum GNUNET_BLOCK_Type type,
686 PluginDatumProcessor proc,
689 struct Plugin *plugin = cls;
690 uint32_t utype = type;
691 struct GNUNET_PQ_QueryParam params[] = {
692 GNUNET_PQ_query_param_uint32 (&utype),
693 GNUNET_PQ_query_param_uint64 (&offset),
694 GNUNET_PQ_query_param_end
698 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
699 "select_non_anonymous",
702 process_result (plugin,
710 * Context for #repl_iter() function.
718 struct Plugin *plugin;
721 * Function to call for the result (or the NULL).
723 PluginDatumProcessor proc;
726 * Closure for @e proc.
733 * Wrapper for the iterator for 'sqlite_plugin_replication_get'.
734 * Decrements the replication counter and calls the original
737 * @param cls closure with the `struct ReplCtx *`
738 * @param key key for the content
739 * @param size number of bytes in @a data
740 * @param data content stored
741 * @param type type of the content
742 * @param priority priority of the content
743 * @param anonymity anonymity-level for the content
744 * @param expiration expiration time for the content
745 * @param uid unique identifier for the datum;
746 * maybe 0 if no unique identifier is available
747 * @return #GNUNET_SYSERR to abort the iteration,
748 * #GNUNET_OK to continue
749 * (continue on call to "next", of course),
750 * #GNUNET_NO to delete the item and continue (if supported)
753 repl_proc (void *cls,
754 const struct GNUNET_HashCode *key,
757 enum GNUNET_BLOCK_Type type,
760 struct GNUNET_TIME_Absolute expiration,
763 struct ReplCtx *rc = cls;
764 struct Plugin *plugin = rc->plugin;
766 uint32_t oid = (uint32_t) uid;
767 struct GNUNET_PQ_QueryParam params[] = {
768 GNUNET_PQ_query_param_uint32 (&oid),
769 GNUNET_PQ_query_param_end
773 ret = rc->proc (rc->proc_cls,
782 qret = GNUNET_PQ_exec_prepared (plugin->dbh,
786 GNUNET_POSTGRES_check_result (plugin->dbh,
791 return GNUNET_SYSERR;
798 * Get a random item for replication. Returns a single, not expired,
799 * random item from those with the highest replication counters. The
800 * item's replication counter is decremented by one IF it was positive
801 * before. Call @a proc with all values ZERO or NULL if the datastore
804 * @param cls closure with the `struct Plugin`
805 * @param proc function to call the value (once only).
806 * @param proc_cls closure for @a proc
809 postgres_plugin_get_replication (void *cls,
810 PluginDatumProcessor proc,
813 struct Plugin *plugin = cls;
819 rc.proc_cls = proc_cls;
820 ret = PQexecPrepared (plugin->dbh,
821 "select_replication_order", 0, NULL, NULL,
823 process_result (plugin,
832 * Get a random item for expiration. Call @a proc with all values
833 * ZERO or NULL if the datastore is empty.
835 * @param cls closure with the `struct Plugin`
836 * @param proc function to call the value (once only).
837 * @param proc_cls closure for @a proc
840 postgres_plugin_get_expiration (void *cls,
841 PluginDatumProcessor proc,
844 struct Plugin *plugin = cls;
845 struct GNUNET_TIME_Absolute now;
846 struct GNUNET_PQ_QueryParam params[] = {
847 GNUNET_PQ_query_param_absolute_time (&now),
848 GNUNET_PQ_query_param_end
852 now = GNUNET_TIME_absolute_get ();
853 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
854 "select_expiration_order",
856 process_result (plugin,
864 * Update the priority for a particular key in the datastore. If
865 * the expiration time in value is different than the time found in
866 * the datastore, the higher value should be kept. For the
867 * anonymity level, the lower value is to be used. The specified
868 * priority should be added to the existing priority, ignoring the
871 * Note that it is possible for multiple values to match this put.
872 * In that case, all of the respective values are updated.
874 * @param cls our `struct Plugin *`
875 * @param uid unique identifier of the datum
876 * @param delta by how much should the priority
878 * @param expire new expiration time should be the
879 * MAX of any existing expiration time and
881 * @param cont continuation called with success or failure status
882 * @param cons_cls continuation closure
885 postgres_plugin_update (void *cls,
888 struct GNUNET_TIME_Absolute expire,
889 PluginUpdateCont cont,
892 struct Plugin *plugin = cls;
893 uint32_t oid = (uint32_t) uid;
894 struct GNUNET_PQ_QueryParam params[] = {
895 GNUNET_PQ_query_param_uint32 (&delta),
896 GNUNET_PQ_query_param_absolute_time (&expire),
897 GNUNET_PQ_query_param_uint32 (&oid),
898 GNUNET_PQ_query_param_end
902 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
906 GNUNET_POSTGRES_check_result (plugin->dbh,
925 * Get all of the keys in the datastore.
927 * @param cls closure with the `struct Plugin *`
928 * @param proc function to call on each key
929 * @param proc_cls closure for @a proc
932 postgres_plugin_get_keys (void *cls,
933 PluginKeyProcessor proc,
936 struct Plugin *plugin = cls;
939 struct GNUNET_HashCode key;
942 res = PQexecPrepared (plugin->dbh,
944 0, NULL, NULL, NULL, 1);
945 ret = PQntuples (res);
948 if (sizeof (struct GNUNET_HashCode) !=
949 PQgetlength (res, i, 0))
952 PQgetvalue (res, i, 0),
953 sizeof (struct GNUNET_HashCode));
954 proc (proc_cls, &key, 1);
958 proc (proc_cls, NULL, 0);
965 * @param cls closure with the `struct Plugin *`
968 postgres_plugin_drop (void *cls)
970 struct Plugin *plugin = cls;
973 GNUNET_POSTGRES_exec (plugin->dbh,
975 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
977 _("Failed to drop table from database.\n"));
982 * Entry point for the plugin.
984 * @param cls the `struct GNUNET_DATASTORE_PluginEnvironment*`
985 * @return our `struct Plugin *`
988 libgnunet_plugin_datastore_postgres_init (void *cls)
990 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
991 struct GNUNET_DATASTORE_PluginFunctions *api;
992 struct Plugin *plugin;
994 plugin = GNUNET_new (struct Plugin);
996 if (GNUNET_OK != init_connection (plugin))
998 GNUNET_free (plugin);
1001 api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
1003 api->estimate_size = &postgres_plugin_estimate_size;
1004 api->put = &postgres_plugin_put;
1005 api->update = &postgres_plugin_update;
1006 api->get_key = &postgres_plugin_get_key;
1007 api->get_replication = &postgres_plugin_get_replication;
1008 api->get_expiration = &postgres_plugin_get_expiration;
1009 api->get_zero_anonymity = &postgres_plugin_get_zero_anonymity;
1010 api->get_keys = &postgres_plugin_get_keys;
1011 api->drop = &postgres_plugin_drop;
1012 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1013 "datastore-postgres",
1014 _("Postgres database running\n"));
1020 * Exit point from the plugin.
1022 * @param cls our `struct Plugin *`
1023 * @return always NULL
1026 libgnunet_plugin_datastore_postgres_done (void *cls)
1028 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1029 struct Plugin *plugin = api->cls;
1031 PQfinish (plugin->dbh);
1032 GNUNET_free (plugin);
1037 /* end of plugin_datastore_postgres.c */