lua_graph.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. #include "lua_volksdata.h"
  2. static VOLK_Graph **allocate_graph (lua_State *L)
  3. {
  4. VOLK_Graph **gp = lua_newuserdatauv (L, sizeof (*gp), 1);
  5. luaL_getmetatable (L, "VOLK.Graph");
  6. lua_setmetatable (L, -2);
  7. return gp;
  8. }
  9. /** @brief Create a new graph.
  10. *
  11. * @param[in] store The store to use as a back end. if unspecified, an
  12. * in-memory hashmap is used.
  13. *
  14. * @param[in] uri_str String to use as the graph URI. It may be namespace-
  15. * prefixed, or fully qualified. If not specified, Volksdata generates a
  16. * UUID4 URN.
  17. *
  18. * @param[in[ is_ns Whether the passed string is a namespace-prefixed URI.
  19. * Default is false. If this is true, the `uri_str` parameter MUST be passed.
  20. */
  21. static int l_graph_new (lua_State *L)
  22. {
  23. VOLK_Store *store;
  24. if (lua_isnoneornil (L, 1)) store = NULL;
  25. else store = *(VOLK_Store **)luaL_checkudata (L, 1, "VOLK.Store");
  26. const char *uri_str = lua_tostring (L, 2);
  27. bool is_ns = lua_toboolean (L, 3);
  28. if (is_ns && !uri_str)
  29. luaL_error (L, "URI must be passed if is_ns is true.", 2);
  30. VOLK_Graph **gp = allocate_graph (L);
  31. *gp = is_ns ?
  32. VOLK_graph_new_ns (store, uri_str) :
  33. VOLK_graph_new (store, uri_str);
  34. LUA_NLCHECK (*gp, "Error creating graph.");
  35. LOG_DEBUG ("New graph URI @%p: %s", *gp, VOLK_graph_uri (*gp)->data);
  36. return 1;
  37. }
  38. static int l_graph_list (lua_State *L)
  39. {
  40. const VOLK_Store *store = *(VOLK_Store **)luaL_checkudata (
  41. L, 1, "VOLK.Store");
  42. VOLK_TermSet *ts = VOLK_graph_list (store);
  43. LUA_NLCHECK (ts, "Error retrieving context list.");
  44. return tset_to_udata (L, ts);
  45. }
  46. /*
  47. * Class methods.
  48. */
  49. static int l_graph_gc (lua_State *L)
  50. {
  51. VOLK_Graph **gp = luaL_checkudata(L, 1, "VOLK.Graph");
  52. LOG_DEBUG ("Garbage collecting graph @%p", *gp);
  53. VOLK_graph_free (*gp);
  54. *gp = NULL;
  55. return 0;
  56. }
  57. static int l_graph_get_uri (lua_State *L)
  58. {
  59. const VOLK_Graph *gr = check_graph (L, 1);
  60. VOLK_Term **tp = lua_newuserdata (L, sizeof *tp);
  61. luaL_getmetatable (L, "VOLK.Term");
  62. lua_setmetatable (L, -2);
  63. *tp = VOLK_term_copy (VOLK_graph_uri (gr));
  64. LUA_NLCHECK (*tp, "Error getting graph URI.");
  65. return 1;
  66. }
  67. static int l_graph_to_string (lua_State *L)
  68. {
  69. const VOLK_Graph *gr = check_graph (L, 1);
  70. lua_pushfstring (
  71. L, "VOLK.Graph @%p <%s>: %d triples",
  72. gr, VOLK_graph_uri (gr)->data, VOLK_graph_size (gr));
  73. return 1;
  74. }
  75. static int l_graph_len (lua_State *L)
  76. {
  77. const VOLK_Graph *gr = check_graph (L, 1);
  78. lua_pushinteger (L, VOLK_graph_size (gr));
  79. return 1;
  80. }
  81. static int l_graph_copy (lua_State *L)
  82. {
  83. const VOLK_Graph *src = check_graph (L, 1);
  84. VOLK_Graph *dest = check_graph (L, 2);
  85. const VOLK_Term *s, *p, *o;
  86. if lua_isnoneornil (L, 3) s = NULL;
  87. else s = check_term (L, 3);
  88. if lua_isnoneornil (L, 4) p = NULL;
  89. else p = check_term (L, 4);
  90. if lua_isnoneornil (L, 5) o = NULL;
  91. else o = check_term (L, 5);
  92. LUA_PCHECK (VOLK_graph_copy_contents (
  93. src, dest, s, p, o), "Error copying graph.");
  94. return 1;
  95. }
  96. static int l_graph_equals (lua_State *L)
  97. {
  98. const VOLK_Graph *gr1 = check_graph (L, 1);
  99. const VOLK_Graph *gr2 = check_graph (L, 2);
  100. LOG_DEBUG ("Comparing graphs %p %p", gr1, gr2);
  101. int eq_rc = VOLK_graph_equals (gr1, gr2);
  102. lua_pushboolean (L, eq_rc);
  103. return 1;
  104. }
  105. static int l_graph_contains (lua_State *L)
  106. {
  107. const VOLK_Graph *gr = check_graph (L, 1);
  108. const VOLK_Triple *spo = check_triple (L, 2);
  109. lua_pushboolean (L, VOLK_graph_contains (gr, spo));
  110. return 1;
  111. }
  112. /// Initialize iterative addition.
  113. static int l_graph_add_init (lua_State *L)
  114. {
  115. VOLK_Graph *gr = check_graph (L, 1);
  116. VOLK_GraphIterator **it_p = lua_newuserdata (L, sizeof *it_p);
  117. luaL_getmetatable (L, "VOLK.GraphIterator");
  118. lua_setmetatable (L, -2);
  119. *it_p = VOLK_graph_add_init (gr);
  120. LUA_NLCHECK (*it_p, "Error creating graph iterator.");
  121. return 1;
  122. }
  123. /// Add one triple.
  124. static int l_graph_add_iter (lua_State *L)
  125. {
  126. VOLK_GraphIterator **it_p = luaL_checkudata (L, 1, "VOLK.GraphIterator");
  127. const VOLK_Triple *spo = check_triple (L, 2);
  128. LUA_PCHECK (VOLK_graph_add_iter (*it_p, spo), "Error adding triple");
  129. return 0;
  130. }
  131. /// Finalize iterative addition.
  132. static int l_graph_add_done (lua_State *L)
  133. {
  134. VOLK_GraphIterator **it_p = luaL_checkudata (L, 1, "VOLK.GraphIterator");
  135. VOLK_graph_add_done (*it_p);
  136. *it_p = 0; // it still gets garbage collected, this prevents double-free.
  137. return 0;
  138. }
  139. /** @brief Add triples from an indexed table.
  140. */
  141. static int l_graph_add (lua_State *L)
  142. {
  143. VOLK_Graph *gr = check_graph (L, 1);
  144. int rc;
  145. VOLK_rc volk_rc= VOLK_NOACTION;
  146. size_t i = 0, ct = 0;
  147. VOLK_GraphIterator *it = VOLK_graph_add_init (gr);
  148. LUA_NLCHECK (it, "Error creating iterator.");
  149. while ((rc = lua_rawgeti (L, 2, ++i)) != LUA_TNIL) {
  150. //LOG_DEBUG ("Triple type: %s", lua_typename (L, rc));
  151. const VOLK_Triple *spo = check_triple (L, -1);
  152. LOG_DEBUG (
  153. "Got triple %d: {%s %s %s}\n",
  154. i, spo->s->data, spo->p->data, spo->o->data);
  155. volk_rc = VOLK_graph_add_iter (it, spo);
  156. if (volk_rc < VOLK_OK) break;
  157. if (volk_rc == VOLK_OK) ct++;
  158. };
  159. VOLK_graph_add_done (it);
  160. lua_pushinteger (L, ct);
  161. if (UNLIKELY (volk_rc < VOLK_OK)) return luaL_error (
  162. L, "Error adding triple at position %d: %s",
  163. i, VOLK_strerror (volk_rc));
  164. else return 1;
  165. }
  166. static int l_graph_remove (lua_State *L)
  167. {
  168. VOLK_Graph *gr = check_graph (L, 1);
  169. const VOLK_Term *s, *p, *o;
  170. if lua_isnoneornil (L, 2) s = NULL;
  171. else s = check_term (L, 2);
  172. if lua_isnoneornil (L, 3) p = NULL;
  173. else p = check_term (L, 3);
  174. if lua_isnoneornil (L, 4) o = NULL;
  175. else o = check_term (L, 4);
  176. size_t ct;
  177. VOLK_rc rc = VOLK_graph_remove (gr, s, p, o, &ct);
  178. LUA_PCHECK (rc, "Error removing triples from graph.");
  179. lua_pushinteger (L, ct);
  180. return 1;
  181. }
  182. static int graph_iter_next (lua_State *L)
  183. {
  184. VOLK_GraphIterator **it_p = lua_touserdata (L, lua_upvalueindex (1));
  185. VOLK_Triple **spo_p = lua_newuserdatauv (L, sizeof (*spo_p), 1);
  186. luaL_getmetatable (L, "VOLK.Triple");
  187. lua_setmetatable (L, -2);
  188. *spo_p = NULL;
  189. VOLK_rc rc = VOLK_graph_iter_next (*it_p, spo_p);
  190. if (rc != VOLK_OK) {
  191. VOLK_graph_iter_free (*it_p);
  192. *it_p = NULL;
  193. if (rc == VOLK_END) {
  194. lua_pushnil (L);
  195. lua_pushstring (L, "End of lookup results.");
  196. return 2;
  197. }
  198. LUA_PCHECK (rc, "Error retrieving a lookup result.");
  199. }
  200. return 1;
  201. }
  202. static int l_graph_lookup (lua_State *L)
  203. {
  204. const VOLK_Graph *gr = check_graph (L, 1);
  205. const VOLK_Term *s, *p, *o;
  206. if lua_isnoneornil (L, 2) s = NULL;
  207. else s = check_term (L, 2);
  208. if lua_isnoneornil (L, 3) p = NULL;
  209. else p = check_term (L, 3);
  210. if lua_isnoneornil (L, 4) o = NULL;
  211. else o = check_term (L, 4);
  212. VOLK_GraphIterator **it_p = lua_newuserdata (L, sizeof *it_p);
  213. *it_p = NULL;
  214. luaL_getmetatable (L, "VOLK.GraphIterator");
  215. lua_setmetatable (L, -2);
  216. size_t ct;
  217. *it_p = VOLK_graph_lookup (gr, s, p, o, &ct);
  218. LUA_NLCHECK (*it_p, "Error creating graph iterator.");
  219. LOG_DEBUG ("Found triples: %d", ct);
  220. lua_pushcclosure (L, graph_iter_next, 1);
  221. return 1;
  222. }
  223. static int graph_encode_iter_next (lua_State *L)
  224. {
  225. void *it = lua_touserdata (L, lua_upvalueindex (1));
  226. const VOLK_Codec *codec = lua_touserdata (L, lua_upvalueindex (2));
  227. char *out = NULL;
  228. VOLK_rc rc = codec->encode_graph_iter (it, &out);
  229. LUA_PCHECK (rc, "Encoding failed");
  230. //LOG_DEBUG ("Serialized fragment: %s", out);
  231. if (rc == VOLK_END) {
  232. codec->encode_graph_done (it);
  233. return 0;
  234. }
  235. lua_pushstring (L, out);
  236. free (out);
  237. return 1;
  238. }
  239. static int l_graph_encode_iter (lua_State *L)
  240. {
  241. const VOLK_Graph *gr = check_graph (L, 1);
  242. const char *codec_str = lua_tostring (L, 2);
  243. const VOLK_Codec *codec_p;
  244. if (strcmp(codec_str, "nt") == 0)
  245. codec_p = &nt_codec;
  246. else if (strcmp(codec_str, "ttl") == 0)
  247. codec_p = &ttl_codec;
  248. else
  249. return luaL_error(L, "Invalid encoding format: %s", codec_str);
  250. void *it = codec_p->encode_graph_init (gr);
  251. LUA_NLCHECK (it, "Error creating codec iterator.");
  252. lua_pushlightuserdata (L, it);
  253. lua_pushlightuserdata (L, (void *)codec_p);
  254. lua_pushcclosure (L, graph_encode_iter_next, 2);
  255. return 1;
  256. }
  257. static int l_graph_get (lua_State *L)
  258. {
  259. VOLK_Term *gr_uri = check_term (L, 1);
  260. VOLK_Store *store = *(VOLK_Store **)luaL_checkudata (L, 2, "VOLK.Store");
  261. VOLK_Graph **gp = allocate_graph (L);
  262. size_t ct;
  263. *gp = VOLK_graph_get (store, gr_uri, &ct);
  264. lua_pushinteger (L, ct);
  265. return 2;
  266. }
  267. /** @brief Free an iterator handle.
  268. *
  269. * This is only called if the iterator is abandoned before the
  270. * iteration cycle is over (either by end of loop or error).
  271. *
  272. * @note A new iterator should not be started without first garbage-collecting
  273. * an incomplete one. That would cause a MDB_BAD_RSLOT error on an LMDB-backed
  274. * graph, because it attempts to open a new read transaction within the same
  275. * thread while the old iterator is keeping the old one open. This could be
  276. * fixed with some good judgment.
  277. *
  278. * From the LMDB manual:
  279. *
  280. * > A thread can only use one transaction at a time, plus any child
  281. * > transactions.
  282. */
  283. static int graph_iter_gc (lua_State *L)
  284. {
  285. VOLK_GraphIterator **it_p = lua_touserdata (L, 1);
  286. if (UNLIKELY (!it_p || !*it_p)) return 0;
  287. LOG_DEBUG ("Garbage collecting iterator @%p", it_p);
  288. VOLK_graph_iter_free (*it_p);
  289. *it_p = NULL;
  290. return 0;
  291. }
  292. /** Returns a LinkMap that can be iterated over with iter().
  293. */
  294. static int l_graph_connections (lua_State *L)
  295. {
  296. const VOLK_Graph *gr = check_graph (L, 1);
  297. VOLK_Term *t = check_term (L, 2);
  298. const VOLK_LinkType type = luaL_checkinteger (L, 3);
  299. LOG_DEBUG ("Adding term for connections: @%p", *t);
  300. VOLK_LinkMap *lm = VOLK_graph_connections (gr, t, type);
  301. LUA_NLCHECK (lm, "Error creating link map.");
  302. VOLK_LinkMap **lm_p = lua_newuserdata (L, sizeof *lm_p);
  303. *lm_p = lm;
  304. luaL_getmetatable (L, "VOLK.LinkMap");
  305. lua_setmetatable (L, -2);
  306. LUA_NLCHECK (*lm_p, "Error creating Link map.");
  307. return 1;
  308. }
  309. static int l_graph_term_set (lua_State *L)
  310. {
  311. const VOLK_Graph *gr = check_graph (L, 1);
  312. const VOLK_Term *t1 = check_term (L, 2);
  313. const VOLK_TriplePos t1_pos = luaL_checkinteger (L, 3);
  314. const VOLK_Term *t2 = check_term (L, 4);
  315. const VOLK_TriplePos t2_pos = luaL_checkinteger (L, 5);
  316. VOLK_TermSet *ts = VOLK_graph_term_set (gr, t1, t1_pos, t2, t2_pos);
  317. LUA_NLCHECK (ts, "Error creating term set from graph.");
  318. return tset_to_udata (L, ts);
  319. }
  320. static int l_graph_unique_terms (lua_State *L)
  321. {
  322. const VOLK_Graph *gr = check_graph (L, 1);
  323. const VOLK_TriplePos pos = luaL_checkinteger (L, 2);
  324. VOLK_TermSet *ts = VOLK_graph_unique_terms (gr, pos);
  325. LUA_NLCHECK (ts, "Error creating term set from unique terms.");
  326. return tset_to_udata (L, ts);
  327. }
  328. /** @brief Get all o's for given s and p as a table of values.
  329. *
  330. * @param[in] gr Graph to query from.
  331. *
  332. * @param[in] s Subject to query.
  333. *
  334. * @param[in] p Predicate to query.
  335. *
  336. * @return Table of objects found per sp combination.
  337. *
  338. * @todo This could be reformatted as a term set generator, but it seems more
  339. * efficient this way because it iterates over the results only once.
  340. */
  341. static int l_graph_attr (lua_State *L)
  342. {
  343. const VOLK_Graph *gr = check_graph (L, 1);
  344. const VOLK_Term *s = check_term (L, 2);
  345. const VOLK_Term *p = check_term (L, 3);
  346. VOLK_TermSet *ts = VOLK_graph_term_set (gr, s, TRP_POS_S, p, TRP_POS_P);
  347. LUA_NLCHECK (ts, "Error creating term set from attribute lookup.");
  348. return tset_to_udata (L, ts);
  349. }
  350. /*
  351. * Library setup.
  352. */
  353. static const luaL_Reg graph_lib_fn [] = {
  354. {"new", l_graph_new},
  355. {"list", l_graph_list},
  356. {"get", l_graph_get},
  357. {NULL}
  358. };
  359. /*
  360. static const luaL_Reg graph_getters [] = {
  361. {"uri", l_graph_get_uri},
  362. {"namespace", l_graph_get_nsm},
  363. {NULL}
  364. };
  365. */
  366. /*
  367. static const luaL_Reg graph_setters [] = {
  368. {"uri", l_graph_set_uri},
  369. {NULL}
  370. };
  371. */
  372. static const luaL_Reg graph_lib_meth [] = {
  373. {"__eq", l_graph_equals},
  374. {"__gc", l_graph_gc},
  375. //{"__index", get_attr},
  376. //{"__newindex", set_attr},
  377. {"__tostring", l_graph_to_string},
  378. {"__len", l_graph_len},
  379. {"copy", l_graph_copy},
  380. {"add", l_graph_add},
  381. {"add_init", l_graph_add_init},
  382. {"remove", l_graph_remove},
  383. {"get_uri", l_graph_get_uri},
  384. {"lookup", l_graph_lookup},
  385. {"contains", l_graph_contains},
  386. {"connections", l_graph_connections},
  387. {"term_set", l_graph_term_set},
  388. {"unique_terms", l_graph_unique_terms},
  389. {"attr", l_graph_attr},
  390. {"encode", l_graph_encode_iter},
  391. {NULL}
  392. };
  393. static const LEnumConst graph_enums[] = {
  394. {NULL, 0}
  395. };
  396. static const luaL_Reg graph_iter_meth [] = {
  397. {"add_iter", l_graph_add_iter},
  398. {"add_done", l_graph_add_done},
  399. {"__gc", graph_iter_gc},
  400. };
  401. int luaopen_volksdata_graph (lua_State *L)
  402. {
  403. VOLK_init(); // This is idempotent: no problem calling it multiple times.
  404. luaL_newmetatable (L, "VOLK.Graph");
  405. lua_pushvalue (L, -1);
  406. lua_setfield (L, -2, "__index");
  407. luaL_setfuncs (L, graph_lib_meth, 0);
  408. // Metatables for graph iterator.
  409. luaL_newmetatable (L, "VOLK.GraphIterator");
  410. lua_pushvalue (L, -1);
  411. lua_setfield (L, -2, "__index");
  412. luaL_setfuncs (L, graph_iter_meth, 0);
  413. /*
  414. // Getters table.
  415. lua_newtable (L);
  416. for (int i = 0; graph_getters[i].name != NULL; i++) {
  417. lua_pushcfunction (L, graph_getters[i].func);
  418. lua_setfield (L, -2, graph_getters[i].name);
  419. }
  420. // Set getters table as a value for the Graph metatable.
  421. lua_setfield (L, -2, "getters");
  422. */
  423. luaL_newlib (L, graph_lib_fn);
  424. // Module-level constants.
  425. push_int_const (L, graph_enums);
  426. return 1;
  427. }