123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- #include "lua_volksdata.h"
- static VOLK_Graph **allocate_graph (lua_State *L)
- {
- VOLK_Graph **gp = lua_newuserdatauv (L, sizeof (*gp), 1);
- luaL_getmetatable (L, "VOLK.Graph");
- lua_setmetatable (L, -2);
- return gp;
- }
- /** @brief Create a new graph.
- *
- * @param[in] store The store to use as a back end. if unspecified, an
- * in-memory hashmap is used.
- *
- * @param[in] uri_str String to use as the graph URI. It may be namespace-
- * prefixed, or fully qualified. If not specified, Volksdata generates a
- * UUID4 URN.
- *
- * @param[in[ is_ns Whether the passed string is a namespace-prefixed URI.
- * Default is false. If this is true, the `uri_str` parameter MUST be passed.
- */
- static int l_graph_new (lua_State *L)
- {
- VOLK_Store *store;
- if (lua_isnoneornil (L, 1)) store = NULL;
- else store = *(VOLK_Store **)luaL_checkudata (L, 1, "VOLK.Store");
- const char *uri_str = lua_tostring (L, 2);
- bool is_ns = lua_toboolean (L, 3);
- if (is_ns && !uri_str)
- luaL_error (L, "URI must be passed if is_ns is true.", 2);
- VOLK_Graph **gp = allocate_graph (L);
- *gp = is_ns ?
- VOLK_graph_new_ns (store, uri_str) :
- VOLK_graph_new (store, uri_str);
- LUA_NLCHECK (*gp, "Error creating graph.");
- LOG_DEBUG ("New graph URI @%p: %s", *gp, VOLK_graph_uri (*gp)->data);
- return 1;
- }
- static int l_graph_list (lua_State *L)
- {
- const VOLK_Store *store = *(VOLK_Store **)luaL_checkudata (
- L, 1, "VOLK.Store");
- VOLK_TermSet *ts = VOLK_graph_list (store);
- LUA_NLCHECK (ts, "Error retrieving context list.");
- return tset_to_udata (L, ts);
- }
- /*
- * Class methods.
- */
- static int l_graph_gc (lua_State *L)
- {
- VOLK_Graph **gp = luaL_checkudata(L, 1, "VOLK.Graph");
- LOG_DEBUG ("Garbage collecting graph @%p", *gp);
- VOLK_graph_free (*gp);
- *gp = NULL;
- return 0;
- }
- static int l_graph_get_uri (lua_State *L)
- {
- const VOLK_Graph *gr = check_graph (L, 1);
- VOLK_Term **tp = lua_newuserdata (L, sizeof *tp);
- luaL_getmetatable (L, "VOLK.Term");
- lua_setmetatable (L, -2);
- *tp = VOLK_term_copy (VOLK_graph_uri (gr));
- LUA_NLCHECK (*tp, "Error getting graph URI.");
- return 1;
- }
- static int l_graph_to_string (lua_State *L)
- {
- const VOLK_Graph *gr = check_graph (L, 1);
- lua_pushfstring (
- L, "VOLK.Graph @%p <%s>: %d triples",
- gr, VOLK_graph_uri (gr)->data, VOLK_graph_size (gr));
- return 1;
- }
- static int l_graph_len (lua_State *L)
- {
- const VOLK_Graph *gr = check_graph (L, 1);
- lua_pushinteger (L, VOLK_graph_size (gr));
- return 1;
- }
- static int l_graph_copy (lua_State *L)
- {
- const VOLK_Graph *src = check_graph (L, 1);
- VOLK_Graph *dest = check_graph (L, 2);
- const VOLK_Term *s, *p, *o;
- if lua_isnoneornil (L, 3) s = NULL;
- else s = check_term (L, 3);
- if lua_isnoneornil (L, 4) p = NULL;
- else p = check_term (L, 4);
- if lua_isnoneornil (L, 5) o = NULL;
- else o = check_term (L, 5);
- LUA_PCHECK (VOLK_graph_copy_contents (
- src, dest, s, p, o), "Error copying graph.");
- return 1;
- }
- static int l_graph_equals (lua_State *L)
- {
- const VOLK_Graph *gr1 = check_graph (L, 1);
- const VOLK_Graph *gr2 = check_graph (L, 2);
- LOG_DEBUG ("Comparing graphs %p %p", gr1, gr2);
- int eq_rc = VOLK_graph_equals (gr1, gr2);
- lua_pushboolean (L, eq_rc);
- return 1;
- }
- static int l_graph_contains (lua_State *L)
- {
- const VOLK_Graph *gr = check_graph (L, 1);
- const VOLK_Triple *spo = check_triple (L, 2);
- lua_pushboolean (L, VOLK_graph_contains (gr, spo));
- return 1;
- }
- /// Initialize iterative addition.
- static int l_graph_add_init (lua_State *L)
- {
- VOLK_Graph *gr = check_graph (L, 1);
- VOLK_GraphIterator **it_p = lua_newuserdata (L, sizeof *it_p);
- luaL_getmetatable (L, "VOLK.GraphIterator");
- lua_setmetatable (L, -2);
- *it_p = VOLK_graph_add_init (gr);
- LUA_NLCHECK (*it_p, "Error creating graph iterator.");
- return 1;
- }
- /// Add one triple.
- static int l_graph_add_iter (lua_State *L)
- {
- VOLK_GraphIterator **it_p = luaL_checkudata (L, 1, "VOLK.GraphIterator");
- const VOLK_Triple *spo = check_triple (L, 2);
- LUA_PCHECK (VOLK_graph_add_iter (*it_p, spo), "Error adding triple");
- return 0;
- }
- /// Finalize iterative addition.
- static int l_graph_add_done (lua_State *L)
- {
- VOLK_GraphIterator **it_p = luaL_checkudata (L, 1, "VOLK.GraphIterator");
- VOLK_graph_add_done (*it_p);
- *it_p = 0; // it still gets garbage collected, this prevents double-free.
- return 0;
- }
- /** @brief Add triples from an indexed table.
- */
- static int l_graph_add (lua_State *L)
- {
- VOLK_Graph *gr = check_graph (L, 1);
- int rc;
- VOLK_rc volk_rc= VOLK_NOACTION;
- size_t i = 0, ct = 0;
- VOLK_GraphIterator *it = VOLK_graph_add_init (gr);
- LUA_NLCHECK (it, "Error creating iterator.");
- while ((rc = lua_rawgeti (L, 2, ++i)) != LUA_TNIL) {
- //LOG_DEBUG ("Triple type: %s", lua_typename (L, rc));
- const VOLK_Triple *spo = check_triple (L, -1);
- LOG_DEBUG (
- "Got triple %d: {%s %s %s}\n",
- i, spo->s->data, spo->p->data, spo->o->data);
- volk_rc = VOLK_graph_add_iter (it, spo);
- if (volk_rc < VOLK_OK) break;
- if (volk_rc == VOLK_OK) ct++;
- };
- VOLK_graph_add_done (it);
- lua_pushinteger (L, ct);
- if (UNLIKELY (volk_rc < VOLK_OK)) return luaL_error (
- L, "Error adding triple at position %d: %s",
- i, VOLK_strerror (volk_rc));
- else return 1;
- }
- static int l_graph_remove (lua_State *L)
- {
- VOLK_Graph *gr = check_graph (L, 1);
- const VOLK_Term *s, *p, *o;
- if lua_isnoneornil (L, 2) s = NULL;
- else s = check_term (L, 2);
- if lua_isnoneornil (L, 3) p = NULL;
- else p = check_term (L, 3);
- if lua_isnoneornil (L, 4) o = NULL;
- else o = check_term (L, 4);
- size_t ct;
- VOLK_rc rc = VOLK_graph_remove (gr, s, p, o, &ct);
- LUA_PCHECK (rc, "Error removing triples from graph.");
- lua_pushinteger (L, ct);
- return 1;
- }
- static int graph_iter_next (lua_State *L)
- {
- VOLK_GraphIterator **it_p = lua_touserdata (L, lua_upvalueindex (1));
- VOLK_Triple **spo_p = lua_newuserdatauv (L, sizeof (*spo_p), 1);
- luaL_getmetatable (L, "VOLK.Triple");
- lua_setmetatable (L, -2);
- *spo_p = NULL;
- VOLK_rc rc = VOLK_graph_iter_next (*it_p, spo_p);
- if (rc != VOLK_OK) {
- VOLK_graph_iter_free (*it_p);
- *it_p = NULL;
- if (rc == VOLK_END) {
- lua_pushnil (L);
- lua_pushstring (L, "End of lookup results.");
- return 2;
- }
- LUA_PCHECK (rc, "Error retrieving a lookup result.");
- }
- return 1;
- }
- static int l_graph_lookup (lua_State *L)
- {
- const VOLK_Graph *gr = check_graph (L, 1);
- const VOLK_Term *s, *p, *o;
- if lua_isnoneornil (L, 2) s = NULL;
- else s = check_term (L, 2);
- if lua_isnoneornil (L, 3) p = NULL;
- else p = check_term (L, 3);
- if lua_isnoneornil (L, 4) o = NULL;
- else o = check_term (L, 4);
- VOLK_GraphIterator **it_p = lua_newuserdata (L, sizeof *it_p);
- *it_p = NULL;
- luaL_getmetatable (L, "VOLK.GraphIterator");
- lua_setmetatable (L, -2);
- size_t ct;
- *it_p = VOLK_graph_lookup (gr, s, p, o, &ct);
- LUA_NLCHECK (*it_p, "Error creating graph iterator.");
- LOG_DEBUG ("Found triples: %d", ct);
- lua_pushcclosure (L, graph_iter_next, 1);
- return 1;
- }
- static int graph_encode_iter_next (lua_State *L)
- {
- void *it = lua_touserdata (L, lua_upvalueindex (1));
- const VOLK_Codec *codec = lua_touserdata (L, lua_upvalueindex (2));
- char *out = NULL;
- VOLK_rc rc = codec->encode_graph_iter (it, &out);
- LUA_PCHECK (rc, "Encoding failed");
- //LOG_DEBUG ("Serialized fragment: %s", out);
- if (rc == VOLK_END) {
- codec->encode_graph_done (it);
- return 0;
- }
- lua_pushstring (L, out);
- free (out);
- return 1;
- }
- static int l_graph_encode_iter (lua_State *L)
- {
- const VOLK_Graph *gr = check_graph (L, 1);
- const char *codec_str = lua_tostring (L, 2);
- const VOLK_Codec *codec_p;
- if (strcmp(codec_str, "nt") == 0)
- codec_p = &nt_codec;
- else if (strcmp(codec_str, "ttl") == 0)
- codec_p = &ttl_codec;
- else
- return luaL_error(L, "Invalid encoding format: %s", codec_str);
- void *it = codec_p->encode_graph_init (gr);
- LUA_NLCHECK (it, "Error creating codec iterator.");
- lua_pushlightuserdata (L, it);
- lua_pushlightuserdata (L, (void *)codec_p);
- lua_pushcclosure (L, graph_encode_iter_next, 2);
- return 1;
- }
- static int l_graph_get (lua_State *L)
- {
- VOLK_Term *gr_uri = check_term (L, 1);
- VOLK_Store *store = *(VOLK_Store **)luaL_checkudata (L, 2, "VOLK.Store");
- VOLK_Graph **gp = allocate_graph (L);
- size_t ct;
- *gp = VOLK_graph_get (store, gr_uri, &ct);
- lua_pushinteger (L, ct);
- return 2;
- }
- /** @brief Free an iterator handle.
- *
- * This is only called if the iterator is abandoned before the
- * iteration cycle is over (either by end of loop or error).
- *
- * @note A new iterator should not be started without first garbage-collecting
- * an incomplete one. That would cause a MDB_BAD_RSLOT error on an LMDB-backed
- * graph, because it attempts to open a new read transaction within the same
- * thread while the old iterator is keeping the old one open. This could be
- * fixed with some good judgment.
- *
- * From the LMDB manual:
- *
- * > A thread can only use one transaction at a time, plus any child
- * > transactions.
- */
- static int graph_iter_gc (lua_State *L)
- {
- VOLK_GraphIterator **it_p = lua_touserdata (L, 1);
- if (UNLIKELY (!it_p || !*it_p)) return 0;
- LOG_DEBUG ("Garbage collecting iterator @%p", it_p);
- VOLK_graph_iter_free (*it_p);
- *it_p = NULL;
- return 0;
- }
- /** Returns a LinkMap that can be iterated over with iter().
- */
- static int l_graph_connections (lua_State *L)
- {
- const VOLK_Graph *gr = check_graph (L, 1);
- VOLK_Term *t = check_term (L, 2);
- const VOLK_LinkType type = luaL_checkinteger (L, 3);
- LOG_DEBUG ("Adding term for connections: @%p", *t);
- VOLK_LinkMap *lm = VOLK_graph_connections (gr, t, type);
- LUA_NLCHECK (lm, "Error creating link map.");
- VOLK_LinkMap **lm_p = lua_newuserdata (L, sizeof *lm_p);
- *lm_p = lm;
- luaL_getmetatable (L, "VOLK.LinkMap");
- lua_setmetatable (L, -2);
- LUA_NLCHECK (*lm_p, "Error creating Link map.");
- return 1;
- }
- static int l_graph_term_set (lua_State *L)
- {
- const VOLK_Graph *gr = check_graph (L, 1);
- const VOLK_Term *t1 = check_term (L, 2);
- const VOLK_TriplePos t1_pos = luaL_checkinteger (L, 3);
- const VOLK_Term *t2 = check_term (L, 4);
- const VOLK_TriplePos t2_pos = luaL_checkinteger (L, 5);
- VOLK_TermSet *ts = VOLK_graph_term_set (gr, t1, t1_pos, t2, t2_pos);
- LUA_NLCHECK (ts, "Error creating term set from graph.");
- return tset_to_udata (L, ts);
- }
- static int l_graph_unique_terms (lua_State *L)
- {
- const VOLK_Graph *gr = check_graph (L, 1);
- const VOLK_TriplePos pos = luaL_checkinteger (L, 2);
- VOLK_TermSet *ts = VOLK_graph_unique_terms (gr, pos);
- LUA_NLCHECK (ts, "Error creating term set from unique terms.");
- return tset_to_udata (L, ts);
- }
- /** @brief Get all o's for given s and p as a table of values.
- *
- * @param[in] gr Graph to query from.
- *
- * @param[in] s Subject to query.
- *
- * @param[in] p Predicate to query.
- *
- * @return Table of objects found per sp combination.
- *
- * @todo This could be reformatted as a term set generator, but it seems more
- * efficient this way because it iterates over the results only once.
- */
- static int l_graph_attr (lua_State *L)
- {
- const VOLK_Graph *gr = check_graph (L, 1);
- const VOLK_Term *s = check_term (L, 2);
- const VOLK_Term *p = check_term (L, 3);
- VOLK_TermSet *ts = VOLK_graph_term_set (gr, s, TRP_POS_S, p, TRP_POS_P);
- LUA_NLCHECK (ts, "Error creating term set from attribute lookup.");
- return tset_to_udata (L, ts);
- }
- /*
- * Library setup.
- */
- static const luaL_Reg graph_lib_fn [] = {
- {"new", l_graph_new},
- {"list", l_graph_list},
- {"get", l_graph_get},
- {NULL}
- };
- /*
- static const luaL_Reg graph_getters [] = {
- {"uri", l_graph_get_uri},
- {"namespace", l_graph_get_nsm},
- {NULL}
- };
- */
- /*
- static const luaL_Reg graph_setters [] = {
- {"uri", l_graph_set_uri},
- {NULL}
- };
- */
- static const luaL_Reg graph_lib_meth [] = {
- {"__eq", l_graph_equals},
- {"__gc", l_graph_gc},
- //{"__index", get_attr},
- //{"__newindex", set_attr},
- {"__tostring", l_graph_to_string},
- {"__len", l_graph_len},
- {"copy", l_graph_copy},
- {"add", l_graph_add},
- {"add_init", l_graph_add_init},
- {"remove", l_graph_remove},
- {"get_uri", l_graph_get_uri},
- {"lookup", l_graph_lookup},
- {"contains", l_graph_contains},
- {"connections", l_graph_connections},
- {"term_set", l_graph_term_set},
- {"unique_terms", l_graph_unique_terms},
- {"attr", l_graph_attr},
- {"encode", l_graph_encode_iter},
- {NULL}
- };
- static const LEnumConst graph_enums[] = {
- {NULL, 0}
- };
- static const luaL_Reg graph_iter_meth [] = {
- {"add_iter", l_graph_add_iter},
- {"add_done", l_graph_add_done},
- {"__gc", graph_iter_gc},
- };
- int luaopen_volksdata_graph (lua_State *L)
- {
- VOLK_init(); // This is idempotent: no problem calling it multiple times.
- luaL_newmetatable (L, "VOLK.Graph");
- lua_pushvalue (L, -1);
- lua_setfield (L, -2, "__index");
- luaL_setfuncs (L, graph_lib_meth, 0);
- // Metatables for graph iterator.
- luaL_newmetatable (L, "VOLK.GraphIterator");
- lua_pushvalue (L, -1);
- lua_setfield (L, -2, "__index");
- luaL_setfuncs (L, graph_iter_meth, 0);
- /*
- // Getters table.
- lua_newtable (L);
- for (int i = 0; graph_getters[i].name != NULL; i++) {
- lua_pushcfunction (L, graph_getters[i].func);
- lua_setfield (L, -2, graph_getters[i].name);
- }
- // Set getters table as a value for the Graph metatable.
- lua_setfield (L, -2, "getters");
- */
- luaL_newlib (L, graph_lib_fn);
- // Module-level constants.
- push_int_const (L, graph_enums);
- return 1;
- }
|