plugin_datacache_sqlite.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  1. /*
  2. This file is part of GNUnet
  3. Copyright (C) 2006, 2009, 2015 GNUnet e.V.
  4. GNUnet is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Affero General Public License as published
  6. by the Free Software Foundation, either version 3 of the License,
  7. or (at your option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. SPDX-License-Identifier: AGPL3.0-or-later
  15. */
  16. /**
  17. * @file datacache/plugin_datacache_sqlite.c
  18. * @brief sqlite for an implementation of a database backend for the datacache
  19. * @author Christian Grothoff
  20. */
  21. #include "platform.h"
  22. #include "gnunet_util_lib.h"
  23. #include "gnunet_datacache_plugin.h"
  24. #include "gnunet_sq_lib.h"
  25. #include <sqlite3.h>
  26. #define LOG(kind, ...) GNUNET_log_from (kind, "datacache-sqlite", __VA_ARGS__)
  27. #define LOG_STRERROR_FILE(kind, op, fn) \
  28. GNUNET_log_from_strerror_file (kind, "datacache-sqlite", op, fn)
  29. /**
  30. * How much overhead do we assume per entry in the
  31. * datacache?
  32. */
  33. #define OVERHEAD (sizeof(struct GNUNET_HashCode) + 36)
  34. /**
  35. * Context for all functions in this plugin.
  36. */
  37. struct Plugin
  38. {
  39. /**
  40. * Our execution environment.
  41. */
  42. struct GNUNET_DATACACHE_PluginEnvironment *env;
  43. /**
  44. * Handle to the sqlite database.
  45. */
  46. sqlite3 *dbh;
  47. /**
  48. * Filename used for the DB.
  49. */
  50. char *fn;
  51. /**
  52. * Prepared statement for #sqlite_plugin_put.
  53. */
  54. sqlite3_stmt *insert_stmt;
  55. /**
  56. * Prepared statement for #sqlite_plugin_get.
  57. */
  58. sqlite3_stmt *get_count_stmt;
  59. /**
  60. * Prepared statement for #sqlite_plugin_get.
  61. */
  62. sqlite3_stmt *get_stmt;
  63. /**
  64. * Prepared statement for #sqlite_plugin_del.
  65. */
  66. sqlite3_stmt *del_select_stmt;
  67. /**
  68. * Prepared statement for #sqlite_plugin_del.
  69. */
  70. sqlite3_stmt *del_expired_stmt;
  71. /**
  72. * Prepared statement for #sqlite_plugin_del.
  73. */
  74. sqlite3_stmt *del_stmt;
  75. /**
  76. * Prepared statement for #sqlite_plugin_get_random.
  77. */
  78. sqlite3_stmt *get_random_stmt;
  79. /**
  80. * Prepared statement for #sqlite_plugin_get_closest.
  81. */
  82. sqlite3_stmt *get_closest_stmt;
  83. /**
  84. * Number of key-value pairs in the database.
  85. */
  86. unsigned int num_items;
  87. };
  88. /**
  89. * Log an error message at log-level @a level that indicates
  90. * a failure of the command @a cmd with the error from the database @a db
  91. *
  92. * @param db database handle
  93. * @param level log level
  94. * @param cmd failed command
  95. */
  96. #define LOG_SQLITE(db, level, cmd) \
  97. do \
  98. { \
  99. LOG (level, \
  100. _ ("`%s' failed at %s:%d with error: %s\n"), \
  101. cmd, \
  102. __FILE__, \
  103. __LINE__, \
  104. sqlite3_errmsg (db)); \
  105. } while (0)
  106. /**
  107. * Execute SQL statement.
  108. *
  109. * @param db database handle
  110. * @param cmd SQL command to execute
  111. */
  112. #define SQLITE3_EXEC(db, cmd) \
  113. do \
  114. { \
  115. emsg = NULL; \
  116. if (SQLITE_OK != sqlite3_exec (db, cmd, NULL, NULL, &emsg)) \
  117. { \
  118. LOG (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, \
  119. _ ("`%s' failed at %s:%d with error: %s\n"), \
  120. "sqlite3_exec", \
  121. __FILE__, \
  122. __LINE__, \
  123. emsg); \
  124. sqlite3_free (emsg); \
  125. } \
  126. } while (0)
  127. /**
  128. * @brief Prepare a SQL statement
  129. *
  130. * @param dbh database handle
  131. * @param zsql SQL statement text
  132. * @param[out] ppStmt set to the prepared statement
  133. * @return 0 on success
  134. */
  135. static int
  136. sq_prepare (sqlite3 *dbh,
  137. const char *zSql, /* SQL statement, UTF-8 encoded */
  138. sqlite3_stmt **ppStmt)
  139. { /* OUT: Statement handle */
  140. char *dummy;
  141. return sqlite3_prepare (dbh,
  142. zSql,
  143. strlen (zSql),
  144. ppStmt,
  145. (const char **) &dummy);
  146. }
  147. /**
  148. * Store an item in the datastore.
  149. *
  150. * @param cls closure (our `struct Plugin`)
  151. * @param key key to store @a data under
  152. * @param xor_distance how close is @a key to our PID?
  153. * @param size number of bytes in @a data
  154. * @param data data to store
  155. * @param type type of the value
  156. * @param discard_time when to discard the value in any case
  157. * @param path_info_len number of entries in @a path_info
  158. * @param path_info array of peers that have processed the request
  159. * @return 0 if duplicate, -1 on error, number of bytes used otherwise
  160. */
  161. static ssize_t
  162. sqlite_plugin_put (void *cls,
  163. const struct GNUNET_HashCode *key,
  164. uint32_t xor_distance,
  165. size_t size,
  166. const char *data,
  167. enum GNUNET_BLOCK_Type type,
  168. struct GNUNET_TIME_Absolute discard_time,
  169. unsigned int path_info_len,
  170. const struct GNUNET_PeerIdentity *path_info)
  171. {
  172. struct Plugin *plugin = cls;
  173. uint32_t type32 = type;
  174. struct GNUNET_SQ_QueryParam params[] =
  175. { GNUNET_SQ_query_param_uint32 (&type32),
  176. GNUNET_SQ_query_param_absolute_time (&discard_time),
  177. GNUNET_SQ_query_param_auto_from_type (key),
  178. GNUNET_SQ_query_param_uint32 (&xor_distance),
  179. GNUNET_SQ_query_param_fixed_size (data, size),
  180. GNUNET_SQ_query_param_fixed_size (path_info,
  181. path_info_len
  182. * sizeof(struct GNUNET_PeerIdentity)),
  183. GNUNET_SQ_query_param_end };
  184. LOG (GNUNET_ERROR_TYPE_DEBUG,
  185. "Processing PUT of %u bytes with key `%s' and expiration %s\n",
  186. (unsigned int) size,
  187. GNUNET_h2s (key),
  188. GNUNET_STRINGS_relative_time_to_string (
  189. GNUNET_TIME_absolute_get_remaining (
  190. discard_time),
  191. GNUNET_YES));
  192. if (GNUNET_OK != GNUNET_SQ_bind (plugin->insert_stmt, params))
  193. {
  194. LOG_SQLITE (plugin->dbh,
  195. GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
  196. "sqlite3_bind_xxx");
  197. GNUNET_SQ_reset (plugin->dbh, plugin->insert_stmt);
  198. return -1;
  199. }
  200. if (SQLITE_DONE != sqlite3_step (plugin->insert_stmt))
  201. {
  202. LOG_SQLITE (plugin->dbh,
  203. GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
  204. "sqlite3_step");
  205. GNUNET_SQ_reset (plugin->dbh, plugin->insert_stmt);
  206. return -1;
  207. }
  208. plugin->num_items++;
  209. GNUNET_SQ_reset (plugin->dbh, plugin->insert_stmt);
  210. return size + OVERHEAD;
  211. }
  212. /**
  213. * Iterate over the results for a particular key
  214. * in the datastore.
  215. *
  216. * @param cls closure (our `struct Plugin`)
  217. * @param key
  218. * @param type entries of which type are relevant?
  219. * @param iter maybe NULL (to just count)
  220. * @param iter_cls closure for @a iter
  221. * @return the number of results found
  222. */
  223. static unsigned int
  224. sqlite_plugin_get (void *cls,
  225. const struct GNUNET_HashCode *key,
  226. enum GNUNET_BLOCK_Type type,
  227. GNUNET_DATACACHE_Iterator iter,
  228. void *iter_cls)
  229. {
  230. struct Plugin *plugin = cls;
  231. uint32_t type32 = type;
  232. struct GNUNET_TIME_Absolute now;
  233. struct GNUNET_TIME_Absolute exp;
  234. size_t size;
  235. void *dat;
  236. unsigned int cnt;
  237. uint32_t off;
  238. unsigned int total;
  239. size_t psize;
  240. struct GNUNET_PeerIdentity *path;
  241. struct GNUNET_SQ_QueryParam params_count[] =
  242. { GNUNET_SQ_query_param_auto_from_type (key),
  243. GNUNET_SQ_query_param_uint32 (&type32),
  244. GNUNET_SQ_query_param_absolute_time (&now),
  245. GNUNET_SQ_query_param_end };
  246. struct GNUNET_SQ_QueryParam params_select[] =
  247. { GNUNET_SQ_query_param_auto_from_type (key),
  248. GNUNET_SQ_query_param_uint32 (&type32),
  249. GNUNET_SQ_query_param_absolute_time (&now),
  250. GNUNET_SQ_query_param_uint32 (&off),
  251. GNUNET_SQ_query_param_end };
  252. struct GNUNET_SQ_ResultSpec rs[] =
  253. { GNUNET_SQ_result_spec_variable_size (&dat, &size),
  254. GNUNET_SQ_result_spec_absolute_time (&exp),
  255. GNUNET_SQ_result_spec_variable_size ((void **) &path, &psize),
  256. GNUNET_SQ_result_spec_end };
  257. now = GNUNET_TIME_absolute_get ();
  258. LOG (GNUNET_ERROR_TYPE_DEBUG,
  259. "Processing GET for key `%s'\n",
  260. GNUNET_h2s (key));
  261. if (GNUNET_OK != GNUNET_SQ_bind (plugin->get_count_stmt, params_count))
  262. {
  263. LOG_SQLITE (plugin->dbh,
  264. GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
  265. "sqlite3_bind_xxx");
  266. GNUNET_SQ_reset (plugin->dbh, plugin->get_count_stmt);
  267. return 0;
  268. }
  269. if (SQLITE_ROW != sqlite3_step (plugin->get_count_stmt))
  270. {
  271. LOG_SQLITE (plugin->dbh,
  272. GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
  273. "sqlite_step");
  274. GNUNET_SQ_reset (plugin->dbh, plugin->get_count_stmt);
  275. LOG (GNUNET_ERROR_TYPE_DEBUG,
  276. "No content found when processing GET for key `%s'\n",
  277. GNUNET_h2s (key));
  278. return 0;
  279. }
  280. total = sqlite3_column_int (plugin->get_count_stmt, 0);
  281. GNUNET_SQ_reset (plugin->dbh, plugin->get_count_stmt);
  282. if ((0 == total) || (NULL == iter))
  283. {
  284. if (0 == total)
  285. LOG (GNUNET_ERROR_TYPE_DEBUG,
  286. "No content found when processing GET for key `%s'\n",
  287. GNUNET_h2s (key));
  288. return total;
  289. }
  290. cnt = 0;
  291. off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
  292. while (cnt < total)
  293. {
  294. off = (off + 1) % total;
  295. if (GNUNET_OK != GNUNET_SQ_bind (plugin->get_stmt, params_select))
  296. {
  297. LOG_SQLITE (plugin->dbh,
  298. GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
  299. "sqlite3_bind_xxx");
  300. GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
  301. return cnt;
  302. }
  303. if (SQLITE_ROW != sqlite3_step (plugin->get_stmt))
  304. break;
  305. if (GNUNET_OK != GNUNET_SQ_extract_result (plugin->get_stmt, rs))
  306. {
  307. GNUNET_break (0);
  308. GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
  309. break;
  310. }
  311. if (0 != psize % sizeof(struct GNUNET_PeerIdentity))
  312. {
  313. GNUNET_break (0);
  314. psize = 0;
  315. path = NULL;
  316. }
  317. psize /= sizeof(struct GNUNET_PeerIdentity);
  318. cnt++;
  319. LOG (GNUNET_ERROR_TYPE_DEBUG,
  320. "Found %u-byte result when processing GET for key `%s'\n",
  321. (unsigned int) size,
  322. GNUNET_h2s (key));
  323. if (GNUNET_OK != iter (iter_cls, key, size, dat, type, exp, psize, path))
  324. {
  325. GNUNET_SQ_cleanup_result (rs);
  326. GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
  327. break;
  328. }
  329. GNUNET_SQ_cleanup_result (rs);
  330. GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
  331. }
  332. GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
  333. return cnt;
  334. }
  335. /**
  336. * Delete the entry with the lowest expiration value
  337. * from the datacache right now.
  338. *
  339. * @param cls closure (our `struct Plugin`)
  340. * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  341. */
  342. static int
  343. sqlite_plugin_del (void *cls)
  344. {
  345. struct Plugin *plugin = cls;
  346. uint64_t rowid;
  347. void *data;
  348. size_t dsize;
  349. struct GNUNET_HashCode hc;
  350. struct GNUNET_TIME_Absolute now;
  351. struct GNUNET_SQ_ResultSpec rs[] =
  352. { GNUNET_SQ_result_spec_uint64 (&rowid),
  353. GNUNET_SQ_result_spec_auto_from_type (&hc),
  354. GNUNET_SQ_result_spec_variable_size ((void **) &data, &dsize),
  355. GNUNET_SQ_result_spec_end };
  356. struct GNUNET_SQ_QueryParam params[] = { GNUNET_SQ_query_param_uint64 (
  357. &rowid),
  358. GNUNET_SQ_query_param_end };
  359. struct GNUNET_SQ_QueryParam time_params[] =
  360. { GNUNET_SQ_query_param_absolute_time (&now), GNUNET_SQ_query_param_end };
  361. LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing DEL\n");
  362. now = GNUNET_TIME_absolute_get ();
  363. if (GNUNET_OK != GNUNET_SQ_bind (plugin->del_expired_stmt, time_params))
  364. {
  365. LOG_SQLITE (plugin->dbh,
  366. GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
  367. "sqlite3_bind");
  368. GNUNET_SQ_reset (plugin->dbh, plugin->del_expired_stmt);
  369. return GNUNET_SYSERR;
  370. }
  371. if ((SQLITE_ROW != sqlite3_step (plugin->del_expired_stmt)) ||
  372. (GNUNET_OK != GNUNET_SQ_extract_result (plugin->del_expired_stmt, rs)))
  373. {
  374. GNUNET_SQ_reset (plugin->dbh, plugin->del_expired_stmt);
  375. if (SQLITE_ROW != sqlite3_step (plugin->del_select_stmt))
  376. {
  377. LOG_SQLITE (plugin->dbh,
  378. GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
  379. "sqlite3_step");
  380. GNUNET_SQ_reset (plugin->dbh, plugin->del_select_stmt);
  381. return GNUNET_SYSERR;
  382. }
  383. if (GNUNET_OK != GNUNET_SQ_extract_result (plugin->del_select_stmt, rs))
  384. {
  385. GNUNET_SQ_reset (plugin->dbh, plugin->del_select_stmt);
  386. GNUNET_break (0);
  387. return GNUNET_SYSERR;
  388. }
  389. }
  390. GNUNET_SQ_cleanup_result (rs);
  391. GNUNET_SQ_reset (plugin->dbh, plugin->del_select_stmt);
  392. if (GNUNET_OK != GNUNET_SQ_bind (plugin->del_stmt, params))
  393. {
  394. LOG_SQLITE (plugin->dbh,
  395. GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
  396. "sqlite3_bind");
  397. GNUNET_SQ_reset (plugin->dbh, plugin->del_stmt);
  398. return GNUNET_SYSERR;
  399. }
  400. if (SQLITE_DONE != sqlite3_step (plugin->del_stmt))
  401. {
  402. LOG_SQLITE (plugin->dbh,
  403. GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
  404. "sqlite3_step");
  405. GNUNET_SQ_reset (plugin->dbh, plugin->del_stmt);
  406. return GNUNET_SYSERR;
  407. }
  408. plugin->num_items--;
  409. plugin->env->delete_notify (plugin->env->cls, &hc, dsize + OVERHEAD);
  410. GNUNET_SQ_reset (plugin->dbh, plugin->del_stmt);
  411. return GNUNET_OK;
  412. }
  413. /**
  414. * Obtain a random key-value pair from the datacache.
  415. *
  416. * @param cls closure (our `struct Plugin`)
  417. * @param iter maybe NULL (to just count)
  418. * @param iter_cls closure for @a iter
  419. * @return the number of results found, zero (datacache empty) or one
  420. */
  421. static unsigned int
  422. sqlite_plugin_get_random (void *cls,
  423. GNUNET_DATACACHE_Iterator iter,
  424. void *iter_cls)
  425. {
  426. struct Plugin *plugin = cls;
  427. struct GNUNET_TIME_Absolute exp;
  428. size_t size;
  429. void *dat;
  430. uint32_t off;
  431. size_t psize;
  432. uint32_t type;
  433. struct GNUNET_PeerIdentity *path;
  434. struct GNUNET_HashCode key;
  435. struct GNUNET_SQ_QueryParam params[] = { GNUNET_SQ_query_param_uint32 (&off),
  436. GNUNET_SQ_query_param_end };
  437. struct GNUNET_SQ_ResultSpec rs[] =
  438. { GNUNET_SQ_result_spec_variable_size (&dat, &size),
  439. GNUNET_SQ_result_spec_absolute_time (&exp),
  440. GNUNET_SQ_result_spec_variable_size ((void **) &path, &psize),
  441. GNUNET_SQ_result_spec_auto_from_type (&key),
  442. GNUNET_SQ_result_spec_uint32 (&type),
  443. GNUNET_SQ_result_spec_end };
  444. if (0 == plugin->num_items)
  445. return 0;
  446. if (NULL == iter)
  447. return 1;
  448. off =
  449. GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, plugin->num_items);
  450. if (GNUNET_OK != GNUNET_SQ_bind (plugin->get_random_stmt, params))
  451. {
  452. return 0;
  453. }
  454. if (SQLITE_ROW != sqlite3_step (plugin->get_random_stmt))
  455. {
  456. GNUNET_break (0);
  457. GNUNET_SQ_reset (plugin->dbh, plugin->get_random_stmt);
  458. return 0;
  459. }
  460. if (GNUNET_OK != GNUNET_SQ_extract_result (plugin->get_random_stmt, rs))
  461. {
  462. GNUNET_break (0);
  463. GNUNET_SQ_reset (plugin->dbh, plugin->get_random_stmt);
  464. return 0;
  465. }
  466. if (0 != psize % sizeof(struct GNUNET_PeerIdentity))
  467. {
  468. GNUNET_break (0);
  469. psize = 0;
  470. path = NULL;
  471. }
  472. psize /= sizeof(struct GNUNET_PeerIdentity);
  473. LOG (GNUNET_ERROR_TYPE_DEBUG,
  474. "Found %u-byte result with key %s when processing GET-RANDOM\n",
  475. (unsigned int) size,
  476. GNUNET_h2s (&key));
  477. (void) iter (iter_cls,
  478. &key,
  479. size,
  480. dat,
  481. (enum GNUNET_BLOCK_Type) type,
  482. exp,
  483. psize,
  484. path);
  485. GNUNET_SQ_cleanup_result (rs);
  486. GNUNET_SQ_reset (plugin->dbh, plugin->get_random_stmt);
  487. return 1;
  488. }
  489. /**
  490. * Iterate over the results that are "close" to a particular key in
  491. * the datacache. "close" is defined as numerically larger than @a
  492. * key (when interpreted as a circular address space), with small
  493. * distance.
  494. *
  495. * @param cls closure (internal context for the plugin)
  496. * @param key area of the keyspace to look into
  497. * @param num_results number of results that should be returned to @a iter
  498. * @param iter maybe NULL (to just count)
  499. * @param iter_cls closure for @a iter
  500. * @return the number of results found
  501. */
  502. static unsigned int
  503. sqlite_plugin_get_closest (void *cls,
  504. const struct GNUNET_HashCode *key,
  505. unsigned int num_results,
  506. GNUNET_DATACACHE_Iterator iter,
  507. void *iter_cls)
  508. {
  509. struct Plugin *plugin = cls;
  510. uint32_t num_results32 = num_results;
  511. struct GNUNET_TIME_Absolute now;
  512. struct GNUNET_TIME_Absolute exp;
  513. size_t size;
  514. void *dat;
  515. unsigned int cnt;
  516. size_t psize;
  517. uint32_t type;
  518. struct GNUNET_HashCode hc;
  519. struct GNUNET_PeerIdentity *path;
  520. struct GNUNET_SQ_QueryParam params[] =
  521. { GNUNET_SQ_query_param_auto_from_type (key),
  522. GNUNET_SQ_query_param_absolute_time (&now),
  523. GNUNET_SQ_query_param_uint32 (&num_results32),
  524. GNUNET_SQ_query_param_end };
  525. struct GNUNET_SQ_ResultSpec rs[] =
  526. { GNUNET_SQ_result_spec_variable_size (&dat, &size),
  527. GNUNET_SQ_result_spec_absolute_time (&exp),
  528. GNUNET_SQ_result_spec_variable_size ((void **) &path, &psize),
  529. GNUNET_SQ_result_spec_uint32 (&type),
  530. GNUNET_SQ_result_spec_auto_from_type (&hc),
  531. GNUNET_SQ_result_spec_end };
  532. now = GNUNET_TIME_absolute_get ();
  533. LOG (GNUNET_ERROR_TYPE_DEBUG,
  534. "Processing GET_CLOSEST for key `%s'\n",
  535. GNUNET_h2s (key));
  536. if (GNUNET_OK != GNUNET_SQ_bind (plugin->get_closest_stmt, params))
  537. {
  538. LOG_SQLITE (plugin->dbh,
  539. GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
  540. "sqlite3_bind_xxx");
  541. GNUNET_SQ_reset (plugin->dbh, plugin->get_closest_stmt);
  542. return 0;
  543. }
  544. cnt = 0;
  545. while (SQLITE_ROW == sqlite3_step (plugin->get_closest_stmt))
  546. {
  547. if (GNUNET_OK != GNUNET_SQ_extract_result (plugin->get_closest_stmt, rs))
  548. {
  549. GNUNET_break (0);
  550. break;
  551. }
  552. if (0 != psize % sizeof(struct GNUNET_PeerIdentity))
  553. {
  554. GNUNET_break (0);
  555. psize = 0;
  556. path = NULL;
  557. }
  558. psize /= sizeof(struct GNUNET_PeerIdentity);
  559. cnt++;
  560. LOG (GNUNET_ERROR_TYPE_DEBUG,
  561. "Found %u-byte result at %s when processing GET_CLOSE\n",
  562. (unsigned int) size,
  563. GNUNET_h2s (&hc));
  564. if (GNUNET_OK != iter (iter_cls, &hc, size, dat, type, exp, psize, path))
  565. {
  566. GNUNET_SQ_cleanup_result (rs);
  567. break;
  568. }
  569. GNUNET_SQ_cleanup_result (rs);
  570. }
  571. GNUNET_SQ_reset (plugin->dbh, plugin->get_closest_stmt);
  572. return cnt;
  573. }
  574. /**
  575. * Entry point for the plugin.
  576. *
  577. * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironment`)
  578. * @return the plugin's closure (our `struct Plugin`)
  579. */
  580. void *
  581. libgnunet_plugin_datacache_sqlite_init (void *cls)
  582. {
  583. struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
  584. struct GNUNET_DATACACHE_PluginFunctions *api;
  585. struct Plugin *plugin;
  586. char *fn;
  587. char *fn_utf8;
  588. sqlite3 *dbh;
  589. char *emsg;
  590. if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
  591. "datacache-sqlite",
  592. "IN_MEMORY"))
  593. {
  594. if (SQLITE_OK != sqlite3_open (":memory:", &dbh))
  595. return NULL;
  596. fn_utf8 = NULL;
  597. }
  598. else
  599. {
  600. fn = GNUNET_DISK_mktemp ("gnunet-datacache");
  601. if (fn == NULL)
  602. {
  603. GNUNET_break (0);
  604. return NULL;
  605. }
  606. /* fn should be UTF-8-encoded. If it isn't, it's a bug. */
  607. fn_utf8 = GNUNET_strdup (fn);
  608. if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
  609. {
  610. GNUNET_free (fn);
  611. GNUNET_free (fn_utf8);
  612. return NULL;
  613. }
  614. GNUNET_free (fn);
  615. }
  616. SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
  617. SQLITE3_EXEC (dbh, "PRAGMA locking_mode=EXCLUSIVE");
  618. SQLITE3_EXEC (dbh, "PRAGMA journal_mode=OFF");
  619. SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
  620. SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
  621. if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
  622. "datacache-sqlite",
  623. "IN_MEMORY"))
  624. SQLITE3_EXEC (dbh, "PRAGMA sqlite_temp_store=3");
  625. SQLITE3_EXEC (dbh,
  626. "CREATE TABLE ds091 ("
  627. " type INTEGER NOT NULL DEFAULT 0,"
  628. " expire INTEGER NOT NULL,"
  629. " key BLOB NOT NULL DEFAULT '',"
  630. " prox INTEGER NOT NULL,"
  631. " value BLOB NOT NULL,"
  632. " path BLOB DEFAULT '')");
  633. SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds091 (key,type,expire)");
  634. SQLITE3_EXEC (dbh, "CREATE INDEX idx_prox_expire ON ds091 (prox,expire)");
  635. SQLITE3_EXEC (dbh, "CREATE INDEX idx_expire_only ON ds091 (expire)");
  636. plugin = GNUNET_new (struct Plugin);
  637. plugin->env = env;
  638. plugin->dbh = dbh;
  639. plugin->fn = fn_utf8;
  640. if ((SQLITE_OK !=
  641. sq_prepare (plugin->dbh,
  642. "INSERT INTO ds091 (type, expire, key, prox, value, path) "
  643. "VALUES (?, ?, ?, ?, ?, ?)",
  644. &plugin->insert_stmt)) ||
  645. (SQLITE_OK != sq_prepare (plugin->dbh,
  646. "SELECT count(*) FROM ds091 "
  647. "WHERE key=? AND type=? AND expire >= ?",
  648. &plugin->get_count_stmt)) ||
  649. (SQLITE_OK !=
  650. sq_prepare (plugin->dbh,
  651. "SELECT value,expire,path FROM ds091"
  652. " WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET ?",
  653. &plugin->get_stmt)) ||
  654. (SQLITE_OK != sq_prepare (plugin->dbh,
  655. "SELECT _ROWID_,key,value FROM ds091"
  656. " WHERE expire < ?"
  657. " ORDER BY expire ASC LIMIT 1",
  658. &plugin->del_expired_stmt)) ||
  659. (SQLITE_OK != sq_prepare (plugin->dbh,
  660. "SELECT _ROWID_,key,value FROM ds091"
  661. " ORDER BY prox ASC, expire ASC LIMIT 1",
  662. &plugin->del_select_stmt)) ||
  663. (SQLITE_OK != sq_prepare (plugin->dbh,
  664. "DELETE FROM ds091 WHERE _ROWID_=?",
  665. &plugin->del_stmt)) ||
  666. (SQLITE_OK != sq_prepare (plugin->dbh,
  667. "SELECT value,expire,path,key,type FROM ds091 "
  668. "ORDER BY key LIMIT 1 OFFSET ?",
  669. &plugin->get_random_stmt)) ||
  670. (SQLITE_OK !=
  671. sq_prepare (plugin->dbh,
  672. "SELECT value,expire,path,type,key FROM ds091 "
  673. "WHERE key>=? AND expire >= ? ORDER BY KEY ASC LIMIT ?",
  674. &plugin->get_closest_stmt)))
  675. {
  676. LOG_SQLITE (plugin->dbh,
  677. GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
  678. "sq_prepare");
  679. GNUNET_break (SQLITE_OK == sqlite3_close (plugin->dbh));
  680. GNUNET_free (plugin);
  681. return NULL;
  682. }
  683. api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
  684. api->cls = plugin;
  685. api->get = &sqlite_plugin_get;
  686. api->put = &sqlite_plugin_put;
  687. api->del = &sqlite_plugin_del;
  688. api->get_random = &sqlite_plugin_get_random;
  689. api->get_closest = &sqlite_plugin_get_closest;
  690. LOG (GNUNET_ERROR_TYPE_INFO, "Sqlite datacache running\n");
  691. return api;
  692. }
  693. /**
  694. * Exit point from the plugin.
  695. *
  696. * @param cls closure (our `struct Plugin`)
  697. * @return NULL
  698. */
  699. void *
  700. libgnunet_plugin_datacache_sqlite_done (void *cls)
  701. {
  702. struct GNUNET_DATACACHE_PluginFunctions *api = cls;
  703. struct Plugin *plugin = api->cls;
  704. int result;
  705. #if SQLITE_VERSION_NUMBER >= 3007000
  706. sqlite3_stmt *stmt;
  707. #endif
  708. #if ! WINDOWS || defined(__CYGWIN__)
  709. if ((NULL != plugin->fn) && (0 != unlink (plugin->fn)))
  710. LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
  711. GNUNET_free_non_null (plugin->fn);
  712. #endif
  713. sqlite3_finalize (plugin->insert_stmt);
  714. sqlite3_finalize (plugin->get_count_stmt);
  715. sqlite3_finalize (plugin->get_stmt);
  716. sqlite3_finalize (plugin->del_select_stmt);
  717. sqlite3_finalize (plugin->del_expired_stmt);
  718. sqlite3_finalize (plugin->del_stmt);
  719. sqlite3_finalize (plugin->get_random_stmt);
  720. sqlite3_finalize (plugin->get_closest_stmt);
  721. result = sqlite3_close (plugin->dbh);
  722. #if SQLITE_VERSION_NUMBER >= 3007000
  723. if (SQLITE_BUSY == result)
  724. {
  725. LOG (GNUNET_ERROR_TYPE_WARNING,
  726. _ (
  727. "Tried to close sqlite without finalizing all prepared statements.\n"));
  728. stmt = sqlite3_next_stmt (plugin->dbh, NULL);
  729. while (NULL != stmt)
  730. {
  731. result = sqlite3_finalize (stmt);
  732. if (result != SQLITE_OK)
  733. LOG (GNUNET_ERROR_TYPE_WARNING,
  734. "Failed to close statement %p: %d\n",
  735. stmt,
  736. result);
  737. stmt = sqlite3_next_stmt (plugin->dbh, NULL);
  738. }
  739. result = sqlite3_close (plugin->dbh);
  740. }
  741. #endif
  742. if (SQLITE_OK != result)
  743. LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
  744. GNUNET_free (plugin);
  745. GNUNET_free (api);
  746. return NULL;
  747. }
  748. /* end of plugin_datacache_sqlite.c */