+/**
+ * Delete records with the given key
+ *
+ * @param cls closure (internal context for the plugin)
+ * @param sub_system name of sub system
+ * @param peer Peer identity (can be NULL)
+ * @param key entry key string (can be NULL)
+ * @return number of deleted records
+ */
+static int
+peerstore_sqlite_delete_records (void *cls, const char *sub_system,
+ const struct GNUNET_PeerIdentity *peer,
+ const char *key)
+{
+ struct Plugin *plugin = cls;
+ sqlite3_stmt *stmt = plugin->delete_peerstoredata;
+
+ if ((SQLITE_OK !=
+ sqlite3_bind_text (stmt, 1, sub_system, strlen (sub_system) + 1,
+ SQLITE_STATIC)) ||
+ (SQLITE_OK !=
+ sqlite3_bind_blob (stmt, 2, peer, sizeof (struct GNUNET_PeerIdentity),
+ SQLITE_STATIC)) ||
+ (SQLITE_OK !=
+ sqlite3_bind_text (stmt, 3, key, strlen (key) + 1, SQLITE_STATIC)))
+ {
+ LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "sqlite3_bind");
+ }
+ else if (SQLITE_DONE != sqlite3_step (stmt))
+ {
+ LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "sqlite3_step");
+ }
+ if (SQLITE_OK != sqlite3_reset (stmt))
+ {
+ LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "sqlite3_reset");
+ return 0;
+ }
+ return sqlite3_changes (plugin->dbh);
+}
+
+
+/**
+ * Delete expired records (expiry < now)
+ *
+ * @param cls closure (internal context for the plugin)
+ * @param now time to use as reference
+ * @param cont continuation called with the number of records expired
+ * @param cont_cls continuation closure
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and cont is not
+ * called
+ */
+static int
+peerstore_sqlite_expire_records (void *cls, struct GNUNET_TIME_Absolute now,
+ GNUNET_PEERSTORE_Continuation cont,
+ void *cont_cls)
+{
+ struct Plugin *plugin = cls;
+ sqlite3_stmt *stmt = plugin->expire_peerstoredata;
+
+ if (SQLITE_OK !=
+ sqlite3_bind_int64 (stmt, 1, (sqlite3_uint64) now.abs_value_us))
+ {
+ LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "sqlite3_bind");
+ }
+ else if (SQLITE_DONE != sqlite3_step (stmt))
+ {
+ LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "sqlite3_step");
+ }
+ if (SQLITE_OK != sqlite3_reset (stmt))
+ {
+ LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "sqlite3_reset");
+ return GNUNET_SYSERR;
+ }
+ if (NULL != cont)
+ {
+ cont (cont_cls, sqlite3_changes (plugin->dbh));
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Iterate over the records given an optional peer id
+ * and/or key.
+ *
+ * @param cls closure (internal context for the plugin)
+ * @param sub_system name of sub system
+ * @param peer Peer identity (can be NULL)
+ * @param key entry key string (can be NULL)
+ * @param iter function to call asynchronously with the results, terminated
+ * by a NULL result
+ * @param iter_cls closure for @a iter
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not
+ * called
+ */
+static int
+peerstore_sqlite_iterate_records (void *cls, const char *sub_system,
+ const struct GNUNET_PeerIdentity *peer,
+ const char *key,
+ GNUNET_PEERSTORE_Processor iter,
+ void *iter_cls)
+{
+ struct Plugin *plugin = cls;
+ sqlite3_stmt *stmt;
+ int err = 0;
+ int sret;
+ struct GNUNET_PEERSTORE_Record *ret;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Executing iterate request on sqlite db.\n");
+ if (NULL == peer && NULL == key)
+ {
+ stmt = plugin->select_peerstoredata;
+ err =
+ (SQLITE_OK !=
+ sqlite3_bind_text (stmt, 1, sub_system, strlen (sub_system) + 1,
+ SQLITE_STATIC));
+ }
+ else if (NULL == key)
+ {
+ stmt = plugin->select_peerstoredata_by_pid;
+ err =
+ (SQLITE_OK !=
+ sqlite3_bind_text (stmt, 1, sub_system, strlen (sub_system) + 1,
+ SQLITE_STATIC)) ||
+ (SQLITE_OK !=
+ sqlite3_bind_blob (stmt, 2, peer, sizeof (struct GNUNET_PeerIdentity),
+ SQLITE_STATIC));
+ }
+ else if (NULL == peer)
+ {
+ stmt = plugin->select_peerstoredata_by_key;
+ err =
+ (SQLITE_OK !=
+ sqlite3_bind_text (stmt, 1, sub_system, strlen (sub_system) + 1,
+ SQLITE_STATIC)) ||
+ (SQLITE_OK !=
+ sqlite3_bind_text (stmt, 2, key, strlen (key) + 1, SQLITE_STATIC));
+ }
+ else
+ {
+ stmt = plugin->select_peerstoredata_by_all;
+ err =
+ (SQLITE_OK !=
+ sqlite3_bind_text (stmt, 1, sub_system, strlen (sub_system) + 1,
+ SQLITE_STATIC)) ||
+ (SQLITE_OK !=
+ sqlite3_bind_blob (stmt, 2, peer, sizeof (struct GNUNET_PeerIdentity),
+ SQLITE_STATIC)) ||
+ (SQLITE_OK !=
+ sqlite3_bind_text (stmt, 3, key, strlen (key) + 1, SQLITE_STATIC));
+ }
+
+ if (err)
+ {
+ LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "sqlite3_bind_XXXX");
+ if (SQLITE_OK != sqlite3_reset (stmt))
+ LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "sqlite3_reset");
+ return GNUNET_SYSERR;
+ }
+ while (SQLITE_ROW == (sret = sqlite3_step (stmt)))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Returning a matched record.\n");
+ ret = GNUNET_new (struct GNUNET_PEERSTORE_Record);
+
+ ret->sub_system = (char *) sqlite3_column_text (stmt, 0);
+ ret->peer = (struct GNUNET_PeerIdentity *) sqlite3_column_blob (stmt, 1);
+ ret->key = (char *) sqlite3_column_text (stmt, 2);
+ ret->value = (void *) sqlite3_column_blob (stmt, 3);
+ ret->value_size = sqlite3_column_bytes (stmt, 3);
+ ret->expiry = GNUNET_new (struct GNUNET_TIME_Absolute);
+
+ ret->expiry->abs_value_us = (uint64_t) sqlite3_column_int64 (stmt, 4);
+ if (NULL != iter)
+ iter (iter_cls, ret, NULL);
+ GNUNET_free (ret->expiry);
+ GNUNET_free (ret);
+ }
+ if (SQLITE_DONE != sret)
+ {
+ LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite_step");
+ err = 1;
+ }
+ if (SQLITE_OK != sqlite3_reset (stmt))
+ {
+ LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "sqlite3_reset");
+ err = 1;
+ }
+ if (NULL != iter)
+ {
+ iter (iter_cls, NULL, err ? "sqlite error" : NULL);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Store a record in the peerstore.
+ * Key is the combination of sub system and peer identity.
+ * One key can store multiple values.
+ *
+ * @param cls closure (internal context for the plugin)
+ * @param sub_system name of the GNUnet sub system responsible
+ * @param peer peer identity
+ * @param key record key string
+ * @param value value to be stored
+ * @param size size of value to be stored
+ * @param expiry absolute time after which the record is (possibly) deleted
+ * @param options options related to the store operation
+ * @param cont continuation called when record is stored
+ * @param cont_cls continuation closure
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR and cont is not called
+ */
+static int
+peerstore_sqlite_store_record (void *cls, const char *sub_system,
+ const struct GNUNET_PeerIdentity *peer,
+ const char *key, const void *value, size_t size,
+ struct GNUNET_TIME_Absolute expiry,
+ enum GNUNET_PEERSTORE_StoreOption options,
+ GNUNET_PEERSTORE_Continuation cont,
+ void *cont_cls)
+{
+ struct Plugin *plugin = cls;
+ sqlite3_stmt *stmt = plugin->insert_peerstoredata;
+
+ if (GNUNET_PEERSTORE_STOREOPTION_REPLACE == options)
+ {
+ peerstore_sqlite_delete_records (cls, sub_system, peer, key);
+ }
+ if (SQLITE_OK !=
+ sqlite3_bind_text (stmt, 1, sub_system, strlen (sub_system) + 1,
+ SQLITE_STATIC) ||
+ SQLITE_OK != sqlite3_bind_blob (stmt, 2, peer,
+ sizeof (struct GNUNET_PeerIdentity),
+ SQLITE_STATIC) ||
+ SQLITE_OK != sqlite3_bind_text (stmt, 3, key, strlen (key) + 1,
+ SQLITE_STATIC) ||
+ SQLITE_OK != sqlite3_bind_blob (stmt, 4, value, size, SQLITE_STATIC) ||
+ SQLITE_OK != sqlite3_bind_int64 (stmt, 5,
+ (sqlite3_uint64) expiry.abs_value_us))
+ LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "sqlite3_bind");
+ else if (SQLITE_DONE != sqlite3_step (stmt))
+ {
+ LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "sqlite3_step");
+ }
+ if (SQLITE_OK != sqlite3_reset (stmt))
+ {
+ LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "sqlite3_reset");
+ return GNUNET_SYSERR;
+ }
+ if (NULL != cont)
+ {
+ cont (cont_cls, GNUNET_OK);
+ }
+ return GNUNET_OK;
+}
+
+