lmdb_triplestore.pyx 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353
  1. import logging
  2. import sys
  3. import rdflib
  4. #from cython.parallel import prange
  5. from rdflib.graph import DATASET_DEFAULT_GRAPH_ID as RDFLIB_DEFAULT_GRAPH_URI
  6. from lakesuperior.store.base_lmdb_store import (
  7. KeyExistsError, KeyNotFoundError, LmdbError)
  8. from libc.stdlib cimport malloc, free
  9. cimport lakesuperior.cy_include.collections as cc
  10. cimport lakesuperior.cy_include.cylmdb as lmdb
  11. from lakesuperior.model.base cimport (
  12. FIRST_KEY, KLEN, DBL_KLEN, TRP_KLEN, QUAD_KLEN,
  13. Key, DoubleKey, TripleKey, QuadKey,
  14. Buffer, buffer_dump
  15. )
  16. from lakesuperior.store.base_lmdb_store cimport (
  17. _check, BaseLmdbStore, data_v, dbi, key_v)
  18. from lakesuperior.model.rdf.graph cimport Graph
  19. from lakesuperior.model.rdf.term cimport (
  20. Term, deserialize_to_rdflib, serialize_from_rdflib)
  21. from lakesuperior.model.rdf.triple cimport BufferTriple
  22. from lakesuperior.model.structures.hash cimport (
  23. HLEN_128 as HLEN, Hash128, hash128)
  24. # Integer keys and values are stored in the system's native byte order.
  25. # Therefore they must be parsed left-to-right if the system is big-endian,
  26. # and right-to-left if little-endian, in order to maintain the correct
  27. # sorting order.
  28. BIG_ENDIAN = sys.byteorder == 'big'
  29. LSUP_REVERSEKEY = 0 if BIG_ENDIAN else lmdb.MDB_REVERSEKEY
  30. LSUP_REVERSEDUP = 0 if BIG_ENDIAN else lmdb.MDB_REVERSEDUP
  31. INT_KEY_MASK = lmdb.MDB_INTEGERKEY | LSUP_REVERSEKEY
  32. INT_DUP_KEY_MASK = (
  33. lmdb.MDB_DUPSORT | lmdb.MDB_DUPFIXED | lmdb.MDB_INTEGERKEY
  34. | LSUP_REVERSEKEY | LSUP_REVERSEDUP
  35. )
  36. INT_DUP_MASK = (
  37. lmdb.MDB_DUPSORT | lmdb.MDB_DUPFIXED | lmdb.MDB_INTEGERDUP
  38. | LSUP_REVERSEKEY | LSUP_REVERSEDUP
  39. )
  40. lookup_rank = [0, 2, 1]
  41. """
  42. Order in which keys are looked up if two terms are bound.
  43. The indices with the smallest average number of values per key should be
  44. looked up first.
  45. 0 = s:po
  46. 1 = p:so
  47. 2 = o:sp
  48. If we want to get fancy, this can be rebalanced from time to time by
  49. looking up the number of keys in (s:po, p:so, o:sp).
  50. """
  51. lookup_ordering = [
  52. [0, 1, 2], # spo
  53. [1, 0, 2], # pso
  54. [2, 0, 1], # osp
  55. ]
  56. lookup_ordering_2bound = [
  57. [1, 2, 0], # po:s
  58. [0, 2, 1], # so:p
  59. [0, 1, 2], # sp:o
  60. ]
  61. logger = logging.getLogger(__name__)
  62. cdef class LmdbTriplestore(BaseLmdbStore):
  63. """
  64. Low-level storage layer.
  65. This class extends the RDFLib-compatible :py:class:`BaseLmdbStore` and maps
  66. triples and contexts to key-value records in LMDB.
  67. This class uses the original LMDB C API rather than the Python bindings,
  68. because several data manipulations happen after retrieval from the store,
  69. which are more efficiently performed at the C level.
  70. """
  71. dbi_labels = [
  72. # Main data
  73. # Term key to serialized term content
  74. 't:st',
  75. # Joined triple keys to context key
  76. 'spo:c',
  77. # This has empty values and is used to keep track of empty contexts.
  78. 'c:',
  79. # Prefix to namespace
  80. 'pfx:ns',
  81. # Indices
  82. # Namespace to prefix
  83. 'ns:pfx',
  84. # Term hash to triple key
  85. 'th:t',
  86. # Lookups
  87. 's:po',
  88. 'p:so',
  89. 'o:sp',
  90. 'po:s',
  91. 'so:p',
  92. 'sp:o',
  93. 'c:spo',
  94. ]
  95. lookup_indices = [
  96. b's:po',
  97. b'p:so',
  98. b'o:sp',
  99. b'po:s',
  100. b'so:p',
  101. b'sp:o',
  102. ]
  103. dbi_flags = {
  104. 'c': INT_KEY_MASK,
  105. 't:st': INT_KEY_MASK,
  106. 's:po': INT_DUP_KEY_MASK,
  107. 'p:so': INT_DUP_KEY_MASK,
  108. 'o:sp': INT_DUP_KEY_MASK,
  109. 'po:s': INT_DUP_MASK,
  110. 'so:p': INT_DUP_MASK,
  111. 'sp:o': INT_DUP_MASK,
  112. 'c:spo': INT_DUP_KEY_MASK,
  113. 'spo:c': INT_DUP_MASK,
  114. }
  115. logger.debug(f'DBI flags: {dbi_flags}')
  116. flags = 0
  117. options = {
  118. 'map_size': 1024 ** 4 # 1Tb.
  119. }
  120. # DB management methods.
  121. cpdef dict stats(self):
  122. """
  123. Gather statistics about the database."""
  124. st = self._stats()
  125. st['num_triples'] = st['db_stats']['spo:c']['ms_entries']
  126. return st
  127. cpdef size_t _len(self, context=None) except -1:
  128. """
  129. Return the length of the dataset.
  130. The RDFLib interface defines `__len__` in a nonstandard way that
  131. causes a Cython compilation error, so this method is called by the
  132. `__len__` method of its Python counterpart.
  133. """
  134. cdef:
  135. size_t ct
  136. Key ck
  137. if context is not None:
  138. ck = self.to_key(context)
  139. key_v.mv_data = &ck
  140. key_v.mv_size = KLEN
  141. cur = self._cur_open('c:spo')
  142. try:
  143. _check(lmdb.mdb_cursor_get(
  144. cur, &key_v, NULL, lmdb.MDB_SET))
  145. _check(lmdb.mdb_cursor_count(cur, &ct))
  146. except KeyNotFoundError:
  147. return 0
  148. else:
  149. return ct
  150. finally:
  151. #pass
  152. self._cur_close(cur)
  153. else:
  154. return self.stats()['num_triples']
  155. ## PRIVATE METHODS ##
  156. # Triple and graph methods.
  157. cpdef void add(self, triple, context=None, quoted=False) except *:
  158. """
  159. Add a triple and start indexing.
  160. :param tuple(rdflib.Identifier) triple: Tuple of three identifiers.
  161. :param context: Context identifier. ``None`` inserts in the default
  162. graph.
  163. :type context: rdflib.Identifier or None
  164. :param bool quoted: Not used.
  165. """
  166. cdef:
  167. lmdb.MDB_cursor *icur
  168. lmdb.MDB_val spo_v, c_v, null_v, key_v, data_v
  169. unsigned char i
  170. Hash128 thash
  171. QuadKey spock
  172. Buffer pk_t
  173. c = self._normalize_context(context)
  174. if c is None:
  175. c = RDFLIB_DEFAULT_GRAPH_URI
  176. s, p, o = triple
  177. icur = self._cur_open('th:t')
  178. try:
  179. for i, term_obj in enumerate((s, p, o, c)):
  180. serialize_from_rdflib(term_obj, &pk_t)
  181. hash128(&pk_t, &thash)
  182. try:
  183. key_v.mv_data = thash
  184. key_v.mv_size = HLEN
  185. _check(lmdb.mdb_get(
  186. self.txn, self.get_dbi('th:t'), &key_v, &data_v))
  187. spock[i] = (<Key*>data_v.mv_data)[0]
  188. except KeyNotFoundError:
  189. # If term_obj is not found, add it...
  190. logger.debug('Hash {} not found. Adding to DB.'.format(
  191. thash[: HLEN]))
  192. spock[i] = self._append(&pk_t, dblabel=b't:st')
  193. # ...and index it.
  194. key_v.mv_data = thash
  195. key_v.mv_size = HLEN
  196. data_v.mv_data = spock + i
  197. data_v.mv_size = KLEN
  198. _check(
  199. lmdb.mdb_cursor_put(icur, &key_v, &data_v, 0),
  200. 'Error setting key {}.'.format(thash))
  201. finally:
  202. self._cur_close(icur)
  203. spo_v.mv_data = spock # address of sk in spock
  204. spo_v.mv_size = TRP_KLEN # Grab 3 keys
  205. c_v.mv_data = spock + 3 # address of ck in spock
  206. c_v.mv_size = KLEN
  207. null_v.mv_data = b''
  208. null_v.mv_size = 0
  209. try:
  210. _check(lmdb.mdb_put(
  211. self.txn, self.get_dbi('c:'), &c_v, &null_v,
  212. lmdb.MDB_NOOVERWRITE))
  213. except KeyExistsError:
  214. pass
  215. try:
  216. # Add triple:context association.
  217. _check(lmdb.mdb_put(
  218. self.txn, self.get_dbi('spo:c'), &spo_v, &c_v,
  219. lmdb.MDB_NODUPDATA))
  220. except KeyExistsError:
  221. pass
  222. try:
  223. # Index context:triple association.
  224. _check(lmdb.mdb_put(
  225. self.txn, self.get_dbi('c:spo'), &c_v, &spo_v,
  226. lmdb.MDB_NODUPDATA))
  227. except KeyExistsError:
  228. pass
  229. self._index_triple(IDX_OP_ADD, [spock[0], spock[1], spock[2]])
  230. cpdef void add_graph(self, c) except *:
  231. """
  232. Add a graph (context) to the database.
  233. This creates an empty graph by associating the graph URI with the
  234. pickled `None` value. This prevents from removing the graph when all
  235. triples are removed.
  236. :param rdflib.URIRef graph: URI of the named graph to add.
  237. """
  238. cdef:
  239. lmdb.MDB_txn *_txn
  240. Buffer _sc
  241. Key ck
  242. c = self._normalize_context(c)
  243. ck = self.to_key(c)
  244. if not self._key_exists(<unsigned char*>&ck, KLEN, b'c:'):
  245. # Insert context term if not existing.
  246. if self.is_txn_rw:
  247. #logger.debug('Working in existing RW transaction.')
  248. _txn = self.txn
  249. else:
  250. #logger.debug('Opening a temporary RW transaction.')
  251. _check(lmdb.mdb_txn_begin(self.dbenv, NULL, 0, &_txn))
  252. # Open new R/W transactions.
  253. try:
  254. # Add to list of contexts.
  255. key_v.mv_data = &ck
  256. key_v.mv_size = KLEN
  257. data_v.mv_data = &ck # Whatever, length is zero anyways
  258. data_v.mv_size = 0
  259. _check(lmdb.mdb_put(
  260. _txn, self.get_dbi(b'c:'), &key_v, &data_v, 0
  261. ))
  262. if not self.is_txn_rw:
  263. _check(lmdb.mdb_txn_commit(_txn))
  264. # Kick the main transaction to see the new terms.
  265. lmdb.mdb_txn_reset(self.txn)
  266. _check(lmdb.mdb_txn_renew(self.txn))
  267. except:
  268. if not self.is_txn_rw:
  269. lmdb.mdb_txn_abort(_txn)
  270. raise
  271. cpdef void _remove(self, tuple triple_pattern, context=None) except *:
  272. cdef:
  273. lmdb.MDB_val spok_v, ck_v
  274. TripleKey spok_cur
  275. Key ck
  276. if context is not None:
  277. try:
  278. ck = self.to_key(context)
  279. except KeyNotFoundError:
  280. # If context is specified but not found, return to avoid
  281. # deleting the wrong triples.
  282. return
  283. # Get the matching pattern.
  284. match_set = self.triple_keys(triple_pattern, context)
  285. dcur = self._cur_open('spo:c')
  286. icur = self._cur_open('c:spo')
  287. try:
  288. spok_v.mv_size = TRP_KLEN
  289. # If context was specified, remove only associations with that context.
  290. match_set.keys.seek()
  291. if context is not None:
  292. ck_v.mv_data = &ck
  293. ck_v.mv_size = KLEN
  294. while match_set.keys.get_next(&spok_cur):
  295. spok_v.mv_data = spok_cur
  296. # Delete spo:c entry.
  297. try:
  298. _check(lmdb.mdb_cursor_get(
  299. dcur, &spok_v, &ck_v, lmdb.MDB_GET_BOTH))
  300. except KeyNotFoundError:
  301. pass
  302. else:
  303. _check(lmdb.mdb_cursor_del(dcur, 0))
  304. # Restore ck after delete.
  305. ck_v.mv_data = &ck
  306. # Delete c:spo entry.
  307. try:
  308. _check(lmdb.mdb_cursor_get(
  309. icur, &ck_v, &spok_v, lmdb.MDB_GET_BOTH))
  310. except KeyNotFoundError:
  311. pass
  312. else:
  313. _check(lmdb.mdb_cursor_del(icur, 0))
  314. # Delete lookup indices, only if no other context
  315. # association is present.
  316. # spok_v has changed on mdb_cursor_del. Restore.
  317. spok_v.mv_data = spok_cur
  318. try:
  319. _check(lmdb.mdb_cursor_get(
  320. dcur, &spok_v, NULL, lmdb.MDB_SET))
  321. except KeyNotFoundError:
  322. self._index_triple(IDX_OP_REMOVE, spok_cur)
  323. # If no context is specified, remove all associations.
  324. else:
  325. logger.debug('Removing triples in all contexts.')
  326. # Loop over all SPO matching the triple pattern.
  327. while match_set.keys.get_next(&spok_cur):
  328. spok_v.mv_data = spok_cur
  329. # Loop over all context associations for this SPO.
  330. try:
  331. _check(lmdb.mdb_cursor_get(
  332. dcur, &spok_v, &ck_v, lmdb.MDB_SET_KEY))
  333. except KeyNotFoundError:
  334. # Move on to the next SPO.
  335. continue
  336. else:
  337. ck = (<Key*>ck_v.mv_data)[0]
  338. while True:
  339. # Delete c:spo association.
  340. try:
  341. _check(lmdb.mdb_cursor_get(
  342. icur, &ck_v, &spok_v, lmdb.MDB_GET_BOTH))
  343. except KeyNotFoundError:
  344. pass
  345. else:
  346. lmdb.mdb_cursor_del(icur, 0)
  347. # Restore the pointer to the deleted SPO.
  348. spok_v.mv_data = spok_cur
  349. # Move on to next associated context.
  350. try:
  351. _check(lmdb.mdb_cursor_get(
  352. dcur, &spok_v, &ck_v, lmdb.MDB_NEXT_DUP))
  353. except KeyNotFoundError:
  354. break
  355. # Then delete the spo:c association.
  356. try:
  357. _check(lmdb.mdb_cursor_get(
  358. dcur, &spok_v, &ck_v, lmdb.MDB_SET))
  359. except KeyNotFoundError:
  360. pass
  361. else:
  362. lmdb.mdb_cursor_del(dcur, lmdb.MDB_NODUPDATA)
  363. self._index_triple(IDX_OP_REMOVE, spok_cur)
  364. finally:
  365. self._cur_close(dcur)
  366. self._cur_close(icur)
  367. cdef void _index_triple(self, int op, TripleKey spok) except *:
  368. """
  369. Update index for a triple and context (add or remove).
  370. :param str op: one of ``IDX_OP_ADD`` or ``IDX_OP_REMOVE``.
  371. :param TripleKey spok: Triple key to index.
  372. """
  373. cdef:
  374. DoubleKey dbl_keys[3]
  375. size_t i = 0
  376. lmdb.MDB_val key_v, dbl_key_v
  377. dbl_keys = [
  378. [spok[1], spok[2]], # pok
  379. [spok[0], spok[2]], # sok
  380. [spok[0], spok[1]], # spk
  381. ]
  382. #logger.debug(f'''Indices:
  383. #spok: {[spok[0], spok[1], spok[2]]}
  384. #sk: {spok[0]}
  385. #pk: {spok[1]}
  386. #ok: {spok[2]}
  387. #pok: {dbl_keys[0]}
  388. #sok: {dbl_keys[1]}
  389. #spk: {dbl_keys[2]}
  390. #''')
  391. key_v.mv_size = KLEN
  392. dbl_key_v.mv_size = DBL_KLEN
  393. #logger.debug('Start indexing: {}.'.format(spok[: TRP_KLEN]))
  394. if op == IDX_OP_REMOVE:
  395. logger.debug(f'Remove {spok[0]} from indices.')
  396. else:
  397. logger.debug(f'Add {spok[0]} to indices.')
  398. while i < 3:
  399. cur1 = self._cur_open(self.lookup_indices[i]) # s:po, p:so, o:sp
  400. cur2 = self._cur_open(self.lookup_indices[i + 3])# po:s, so:p, sp:o
  401. try:
  402. key_v.mv_data = spok + i
  403. dbl_key_v.mv_data = dbl_keys[i]
  404. # Removal op indexing.
  405. if op == IDX_OP_REMOVE:
  406. try:
  407. _check(lmdb.mdb_cursor_get(
  408. cur1, &key_v, &dbl_key_v, lmdb.MDB_GET_BOTH))
  409. except KeyNotFoundError:
  410. pass
  411. else:
  412. _check(lmdb.mdb_cursor_del(cur1, 0))
  413. # Restore pointers after delete.
  414. key_v.mv_data = spok + i
  415. dbl_key_v.mv_data = dbl_keys[i]
  416. try:
  417. _check(lmdb.mdb_cursor_get(
  418. cur2, &dbl_key_v, &key_v, lmdb.MDB_GET_BOTH))
  419. except KeyNotFoundError:
  420. pass
  421. else:
  422. _check(lmdb.mdb_cursor_del(cur2, 0))
  423. # Addition op indexing.
  424. elif op == IDX_OP_ADD:
  425. try:
  426. _check(lmdb.mdb_cursor_put(
  427. cur1, &key_v, &dbl_key_v, lmdb.MDB_NODUPDATA))
  428. except KeyExistsError:
  429. pass
  430. try:
  431. _check(lmdb.mdb_cursor_put(
  432. cur2, &dbl_key_v, &key_v, lmdb.MDB_NODUPDATA))
  433. except KeyExistsError:
  434. pass
  435. else:
  436. raise ValueError(
  437. 'Index operation \'{}\' is not supported.'.format(op))
  438. i += 1
  439. finally:
  440. #pass
  441. self._cur_close(cur1)
  442. self._cur_close(cur2)
  443. cpdef void _remove_graph(self, object gr_uri) except *:
  444. """
  445. Delete a context.
  446. """
  447. cdef:
  448. Hash128 chash
  449. Key ck
  450. lmdb.MDB_val ck_v, chash_v
  451. Buffer pk_c
  452. # Gather information on the graph prior to deletion.
  453. try:
  454. ck = self.to_key(gr_uri)
  455. except KeyNotFoundError:
  456. return
  457. # Remove all triples and indices associated with the graph.
  458. self._remove((None, None, None), gr_uri)
  459. # Remove the graph if it is in triples.
  460. self._remove((gr_uri, None, None))
  461. self._remove((None, None, gr_uri))
  462. # Clean up all terms related to the graph.
  463. serialize_from_rdflib(gr_uri, &pk_c)
  464. hash128(&pk_c, &chash)
  465. ck_v.mv_size = KLEN
  466. chash_v.mv_size = HLEN
  467. try:
  468. ck_v.mv_data = &ck
  469. _check(lmdb.mdb_del(self.txn, self.get_dbi(b'c:'), &ck_v, NULL))
  470. ck_v.mv_data = &ck
  471. _check(lmdb.mdb_del(self.txn, self.get_dbi(b't:st'), &ck_v, NULL))
  472. chash_v.mv_data = chash
  473. _check(lmdb.mdb_del(self.txn, self.get_dbi(b'th:t'), &chash_v, NULL))
  474. except KeyNotFoundError:
  475. pass
  476. # Lookup methods.
  477. def contexts(self, triple=None):
  478. """
  479. Get a list of all contexts.
  480. :rtype: set(URIRef)
  481. """
  482. cdef:
  483. size_t sz, i
  484. Key* match
  485. try:
  486. self.all_contexts(&match, &sz, triple)
  487. ret = set()
  488. for i in range(sz):
  489. ret.add(self.from_key(match[i]))
  490. finally:
  491. free(match)
  492. return ret
  493. def triples(self, triple_pattern, context=None):
  494. """
  495. Generator over matching triples.
  496. :param tuple triple_pattern: 3 RDFLib terms
  497. :param context: Context graph, if available.
  498. :type context: rdflib.Graph or None
  499. :rtype: Iterator
  500. :return: Generator over triples and contexts in which each result has
  501. the following format::
  502. (s, p, o), generator(contexts)
  503. Where the contexts generator lists all context that the triple appears
  504. in.
  505. """
  506. cdef:
  507. size_t i = 0
  508. TripleKey it_cur
  509. lmdb.MDB_val key_v, data_v
  510. # This sounds strange, RDFLib should be passing None at this point,
  511. # but anyway...
  512. context = self._normalize_context(context)
  513. logger.debug(
  514. 'Getting triples for: {}, {}'.format(triple_pattern, context))
  515. rset = self.triple_keys(triple_pattern, context)
  516. #logger.debug('Triple keys found: {}'.format(rset.data[:rset.size]))
  517. cur = self._cur_open('spo:c')
  518. try:
  519. key_v.mv_size = TRP_KLEN
  520. rset.keys.seek()
  521. while rset.keys.get_next(&it_cur):
  522. key_v.mv_data = it_cur
  523. # Get contexts associated with each triple.
  524. contexts = []
  525. # This shall never be MDB_NOTFOUND.
  526. _check(lmdb.mdb_cursor_get(cur, &key_v, &data_v, lmdb.MDB_SET))
  527. while True:
  528. c_uri = self.from_key((<Key*>data_v.mv_data)[0])
  529. contexts.append(
  530. Graph(self, uri=c_uri)
  531. )
  532. try:
  533. _check(lmdb.mdb_cursor_get(
  534. cur, &key_v, &data_v, lmdb.MDB_NEXT_DUP))
  535. except KeyNotFoundError:
  536. break
  537. yield (
  538. (
  539. self.from_key((<Key*>key_v.mv_data)[0]),
  540. self.from_key((<Key*>key_v.mv_data)[1]),
  541. self.from_key((<Key*>key_v.mv_data)[2]),
  542. ),
  543. tuple(contexts)
  544. )
  545. finally:
  546. self._cur_close(cur)
  547. cpdef Graph triple_keys(
  548. self, tuple triple_pattern, context=None, uri=None
  549. ):
  550. """
  551. Top-level lookup method.
  552. This method is used by `triples` which returns native Python tuples,
  553. as well as by other methods that need to iterate and filter triple
  554. keys without incurring in the overhead of converting them to triples.
  555. :param tuple triple_pattern: 3 RDFLib terms
  556. :param context: Context graph or URI, or None.
  557. :type context: rdflib.term.Identifier or None
  558. """
  559. cdef:
  560. size_t ct = 0, i = 0
  561. lmdb.MDB_cursor *icur
  562. lmdb.MDB_val key_v, data_v
  563. Key tk, ck
  564. TripleKey spok
  565. Graph flt_res, ret
  566. if context is not None:
  567. try:
  568. ck = self.to_key(context)
  569. except KeyNotFoundError:
  570. # Context not found.
  571. return Graph(self, uri=uri)
  572. icur = self._cur_open('c:spo')
  573. try:
  574. key_v.mv_data = &ck
  575. key_v.mv_size = KLEN
  576. # s p o c
  577. if all(triple_pattern):
  578. for i, term in enumerate(triple_pattern):
  579. try:
  580. tk = self.to_key(term)
  581. except KeyNotFoundError:
  582. # A term key was not found.
  583. return Graph(self, uri=uri)
  584. spok[i] = tk
  585. data_v.mv_data = spok
  586. data_v.mv_size = TRP_KLEN
  587. try:
  588. _check(lmdb.mdb_cursor_get(
  589. icur, &key_v, &data_v, lmdb.MDB_GET_BOTH))
  590. except KeyNotFoundError:
  591. # Triple not found.
  592. #logger.debug('spok / ck pair not found.')
  593. return Graph(self, uri=uri)
  594. ret = Graph(self, 1, uri=uri)
  595. ret.keys.add(&spok)
  596. return ret
  597. # ? ? ? c
  598. elif not any(triple_pattern):
  599. # Get all triples from the context
  600. try:
  601. _check(lmdb.mdb_cursor_get(
  602. icur, &key_v, &data_v, lmdb.MDB_SET))
  603. except KeyNotFoundError:
  604. # Triple not found.
  605. return Graph(self, uri=uri)
  606. _check(lmdb.mdb_cursor_count(icur, &ct))
  607. ret = Graph(self, ct, uri=uri)
  608. _check(lmdb.mdb_cursor_get(
  609. icur, &key_v, &data_v, lmdb.MDB_GET_MULTIPLE))
  610. while True:
  611. # Loop over page data.
  612. spok_page = <TripleKey*>data_v.mv_data
  613. for i in range(data_v.mv_size // TRP_KLEN):
  614. ret.keys.add(spok_page + i)
  615. try:
  616. # Get next page.
  617. _check(lmdb.mdb_cursor_get(
  618. icur, &key_v, &data_v, lmdb.MDB_NEXT_MULTIPLE))
  619. except KeyNotFoundError:
  620. return ret
  621. # Regular lookup. Filter _lookup() results by context.
  622. else:
  623. try:
  624. res = self._lookup(triple_pattern)
  625. except KeyNotFoundError:
  626. return Graph(self, uri=uri)
  627. key_v.mv_data = &ck
  628. key_v.mv_size = KLEN
  629. data_v.mv_size = TRP_KLEN
  630. flt_res = Graph(self, res.capacity, uri=uri)
  631. res.keys.seek()
  632. while res.keys.get_next(&spok):
  633. data_v.mv_data = spok
  634. try:
  635. # Verify that the triple is associated with the
  636. # context being searched.
  637. _check(lmdb.mdb_cursor_get(
  638. icur, &key_v, &data_v, lmdb.MDB_GET_BOTH))
  639. except KeyNotFoundError:
  640. continue
  641. else:
  642. flt_res.keys.add(&spok)
  643. return flt_res
  644. finally:
  645. self._cur_close(icur)
  646. # Unfiltered lookup. No context checked.
  647. else:
  648. try:
  649. res = self._lookup(triple_pattern)
  650. except KeyNotFoundError:
  651. return Graph(self, uri=uri)
  652. return res
  653. cdef Graph _lookup(self, tuple triple_pattern):
  654. """
  655. Look up triples in the indices based on a triple pattern.
  656. :rtype: Iterator
  657. :return: Matching triple keys.
  658. """
  659. cdef:
  660. size_t ct = 0
  661. lmdb.MDB_stat db_stat
  662. lmdb.MDB_val spok_v, ck_v
  663. TripleKey spok
  664. Key sk, pk, ok, tk1, tk2, tk3
  665. s, p, o = triple_pattern
  666. try:
  667. if s is not None:
  668. sk = self.to_key(s)
  669. if p is not None:
  670. pk = self.to_key(p)
  671. if o is not None:
  672. ok = self.to_key(o)
  673. except KeyNotFoundError:
  674. return Graph(self)
  675. if s is not None:
  676. tk1 = sk
  677. if p is not None:
  678. tk2 = pk
  679. # s p o
  680. if o is not None:
  681. tk3 = ok
  682. spok_v.mv_data = spok
  683. spok_v.mv_size = TRP_KLEN
  684. try:
  685. spok = [tk1, tk2, tk3]
  686. _check(lmdb.mdb_get(
  687. self.txn, self.get_dbi('spo:c'), &spok_v, &ck_v))
  688. except KeyNotFoundError:
  689. return Graph(self)
  690. matches = Graph(self, 1)
  691. matches.keys.add(&spok)
  692. return matches
  693. # s p ?
  694. return self._lookup_2bound(0, 1, [tk1, tk2])
  695. if o is not None: # s ? o
  696. tk2 = ok
  697. return self._lookup_2bound(0, 2, [tk1, tk2])
  698. # s ? ?
  699. return self._lookup_1bound(0, tk1)
  700. if p is not None:
  701. tk1 = pk
  702. if o is not None: # ? p o
  703. tk2 = ok
  704. return self._lookup_2bound(1, 2, [tk1, tk2])
  705. # ? p ?
  706. return self._lookup_1bound(1, tk1)
  707. if o is not None: # ? ? o
  708. tk1 = ok
  709. return self._lookup_1bound(2, tk1)
  710. # ? ? ?
  711. # Get all triples in the database.
  712. dcur = self._cur_open('spo:c')
  713. try:
  714. _check(
  715. lmdb.mdb_stat(
  716. self.txn, lmdb.mdb_cursor_dbi(dcur), &db_stat
  717. ), 'Error gathering DB stats.'
  718. )
  719. ct = db_stat.ms_entries
  720. ret = Graph(self, ct)
  721. #logger.debug(f'Triples found: {ct}')
  722. if ct == 0:
  723. return Graph(self)
  724. _check(lmdb.mdb_cursor_get(
  725. dcur, &key_v, &data_v, lmdb.MDB_FIRST))
  726. while True:
  727. spok = <TripleKey>key_v.mv_data
  728. ret.keys.add(&spok)
  729. try:
  730. _check(lmdb.mdb_cursor_get(
  731. dcur, &key_v, &data_v, lmdb.MDB_NEXT_NODUP))
  732. except KeyNotFoundError:
  733. break
  734. return ret
  735. finally:
  736. self._cur_close(dcur)
  737. cdef Graph _lookup_1bound(self, unsigned char idx, Key luk):
  738. """
  739. Lookup triples for a pattern with one bound term.
  740. :param str idx_name: The index to look up as one of the keys of
  741. ``_lookup_ordering``.
  742. :param rdflib.URIRef term: Bound term to search for.
  743. :rtype: Iterator(bytes)
  744. :return: SPO keys matching the pattern.
  745. """
  746. cdef:
  747. unsigned int dbflags
  748. unsigned char term_order[3]
  749. size_t ct, i
  750. lmdb.MDB_cursor *icur
  751. lmdb.MDB_val key_v, data_v
  752. TripleKey spok
  753. logger.debug(f'lookup 1bound: {idx}, {luk}')
  754. term_order = lookup_ordering[idx]
  755. icur = self._cur_open(self.lookup_indices[idx])
  756. logging.debug(f'DB label: {self.lookup_indices[idx]}')
  757. logging.debug('term order: {}'.format(term_order[: 3]))
  758. try:
  759. key_v.mv_data = &luk
  760. key_v.mv_size = KLEN
  761. _check(lmdb.mdb_cursor_get(icur, &key_v, &data_v, lmdb.MDB_SET))
  762. _check(lmdb.mdb_cursor_count(icur, &ct))
  763. # Allocate memory for results.
  764. ret = Graph(self, ct)
  765. _check(lmdb.mdb_cursor_get(icur, &key_v, &data_v, lmdb.MDB_SET))
  766. _check(lmdb.mdb_cursor_get(
  767. icur, &key_v, &data_v, lmdb.MDB_GET_MULTIPLE))
  768. while True:
  769. lu_dset = <DoubleKey*>data_v.mv_data
  770. for i in range(data_v.mv_size // DBL_KLEN):
  771. spok[term_order[0]] = luk
  772. spok[term_order[1]] = lu_dset[i][0]
  773. spok[term_order[2]] = lu_dset[i][1]
  774. ret.keys.add(&spok)
  775. try:
  776. # Get results by the page.
  777. _check(lmdb.mdb_cursor_get(
  778. icur, &key_v, &data_v, lmdb.MDB_NEXT_MULTIPLE))
  779. except KeyNotFoundError:
  780. return ret
  781. finally:
  782. self._cur_close(icur)
  783. cdef Graph _lookup_2bound(
  784. self, unsigned char idx1, unsigned char idx2, DoubleKey tks
  785. ):
  786. """
  787. Look up triples for a pattern with two bound terms.
  788. :param str idx1: The index to look up as one of the keys of
  789. ``lookup_ordering_2bound``.
  790. :param rdflib.URIRef term1: First bound term to search for.
  791. :rtype: Iterator(bytes)
  792. :return: SPO keys matching the pattern.
  793. """
  794. cdef:
  795. unsigned char luk1_offset, luk2_offset
  796. unsigned int dbflags
  797. unsigned char term_order[3] # Lookup ordering
  798. size_t ct, i = 0
  799. lmdb.MDB_cursor* icur
  800. Graph ret
  801. DoubleKey luk
  802. TripleKey spok
  803. for i in range(3):
  804. if (
  805. idx1 in lookup_ordering_2bound[i][: 2]
  806. and idx2 in lookup_ordering_2bound[i][: 2]):
  807. term_order = lookup_ordering_2bound[i]
  808. if term_order[0] == idx1:
  809. luk1_offset = 0
  810. luk2_offset = 1
  811. else:
  812. luk1_offset = 1
  813. luk2_offset = 0
  814. dblabel = self.lookup_indices[i + 3] # skip 1bound index labels
  815. break
  816. if i == 2:
  817. raise ValueError(
  818. 'Indices {} and {} not found in LU keys.'.format(
  819. idx1, idx2))
  820. # Compose term keys in lookup key.
  821. luk[luk1_offset] = tks[0]
  822. luk[luk2_offset] = tks[1]
  823. icur = self._cur_open(dblabel)
  824. try:
  825. key_v.mv_data = luk
  826. key_v.mv_size = DBL_KLEN
  827. # Count duplicates for key and allocate memory for result set.
  828. _check(lmdb.mdb_cursor_get(icur, &key_v, &data_v, lmdb.MDB_SET))
  829. _check(lmdb.mdb_cursor_count(icur, &ct))
  830. ret = Graph(self, ct)
  831. _check(lmdb.mdb_cursor_get(icur, &key_v, &data_v, lmdb.MDB_SET))
  832. _check(lmdb.mdb_cursor_get(
  833. icur, &key_v, &data_v, lmdb.MDB_GET_MULTIPLE))
  834. while True:
  835. lu_dset = <Key*>data_v.mv_data
  836. for i in range(data_v.mv_size // KLEN):
  837. spok[term_order[0]] = luk[0]
  838. spok[term_order[1]] = luk[1]
  839. spok[term_order[2]] = lu_dset[i]
  840. ret.keys.add(&spok)
  841. try:
  842. # Get results by the page.
  843. _check(lmdb.mdb_cursor_get(
  844. icur, &key_v, &data_v, lmdb.MDB_NEXT_MULTIPLE))
  845. except KeyNotFoundError:
  846. return ret
  847. finally:
  848. self._cur_close(icur)
  849. cdef void _all_term_keys(self, term_type, cc.HashSet** tkeys) except *:
  850. """
  851. Return all keys of a (``s:po``, ``p:so``, ``o:sp``) index.
  852. """
  853. cdef:
  854. size_t i = 0
  855. lmdb.MDB_stat stat
  856. cc.HashSetConf tkeys_conf
  857. idx_label = self.lookup_indices['spo'.index(term_type)]
  858. icur = self._cur_open(idx_label)
  859. try:
  860. _check(lmdb.mdb_stat(self.txn, lmdb.mdb_cursor_dbi(icur), &stat))
  861. cc.hashset_conf_init(&tkeys_conf)
  862. tkeys_conf.initial_capacity = 1024
  863. tkeys_conf.load_factor = .75
  864. tkeys_conf.key_length = KLEN
  865. tkeys_conf.key_compare = cc.CC_CMP_POINTER
  866. tkeys_conf.hash = cc.POINTER_HASH
  867. cc.hashset_new_conf(&tkeys_conf, tkeys)
  868. try:
  869. _check(lmdb.mdb_cursor_get(
  870. icur, &key_v, NULL, lmdb.MDB_FIRST))
  871. except KeyNotFoundError:
  872. return
  873. while True:
  874. cc.hashset_add(tkeys[0], key_v.mv_data)
  875. rc = lmdb.mdb_cursor_get(
  876. icur, &key_v, NULL, lmdb.MDB_NEXT_NODUP)
  877. try:
  878. _check(rc)
  879. except KeyNotFoundError:
  880. return
  881. i += 1
  882. finally:
  883. self._cur_close(icur)
  884. def all_terms(self, term_type):
  885. """
  886. Return all terms of a type (``s``, ``p``, or ``o``) in the store.
  887. """
  888. cdef:
  889. void* cur
  890. cc.HashSet* tkeys
  891. cc.HashSetIter it
  892. ret = set()
  893. try:
  894. self._all_term_keys(term_type, &tkeys)
  895. cc.hashset_iter_init(&it, tkeys)
  896. while cc.hashset_iter_next(&it, &cur) != cc.CC_ITER_END:
  897. ret.add(self.from_key((<Key*>cur)[0]))
  898. finally:
  899. if tkeys:
  900. free(tkeys)
  901. return ret
  902. cpdef tuple all_namespaces(self):
  903. """
  904. Return all registered namespaces.
  905. """
  906. cdef:
  907. size_t i = 0
  908. lmdb.MDB_stat stat
  909. ret = []
  910. dcur = self._cur_open('pfx:ns')
  911. try:
  912. try:
  913. _check(lmdb.mdb_cursor_get(
  914. dcur, &key_v, &data_v, lmdb.MDB_FIRST))
  915. except KeyNotFoundError:
  916. return tuple()
  917. while True:
  918. ret.append((
  919. (<unsigned char *>key_v.mv_data)[: key_v.mv_size].decode(),
  920. (<unsigned char *>data_v.mv_data)[: data_v.mv_size].decode()))
  921. try:
  922. _check(lmdb.mdb_cursor_get(
  923. dcur, &key_v, &data_v, lmdb.MDB_NEXT))
  924. except KeyNotFoundError:
  925. return tuple(ret)
  926. i += 1
  927. finally:
  928. self._cur_close(dcur)
  929. cdef void all_contexts(
  930. self, Key** ctx, size_t* sz, triple=None
  931. ) except *:
  932. """
  933. Get a list of all contexts.
  934. """
  935. cdef:
  936. size_t ct
  937. lmdb.MDB_stat stat
  938. lmdb.MDB_val key_v, data_v
  939. TripleKey spok
  940. cur = (
  941. self._cur_open('spo:c') if triple and all(triple)
  942. else self._cur_open('c:'))
  943. try:
  944. if triple and all(triple):
  945. _check(lmdb.mdb_stat(
  946. self.txn, lmdb.mdb_cursor_dbi(cur), &stat))
  947. spok = [
  948. self.to_key(triple[0]),
  949. self.to_key(triple[1]),
  950. self.to_key(triple[2]),
  951. ]
  952. key_v.mv_data = spok
  953. key_v.mv_size = TRP_KLEN
  954. try:
  955. _check(lmdb.mdb_cursor_get(
  956. cur, &key_v, &data_v, lmdb.MDB_SET_KEY))
  957. except KeyNotFoundError:
  958. ctx[0] = NULL
  959. return
  960. ctx[0] = <Key*>malloc(stat.ms_entries * KLEN)
  961. sz[0] = 0
  962. while True:
  963. ctx[0][sz[0]] = (<Key*>data_v.mv_data)[0]
  964. sz[0] += 1
  965. try:
  966. _check(lmdb.mdb_cursor_get(
  967. cur, &key_v, &data_v, lmdb.MDB_NEXT_DUP))
  968. except KeyNotFoundError:
  969. break
  970. else:
  971. _check(lmdb.mdb_stat(
  972. self.txn, lmdb.mdb_cursor_dbi(cur), &stat))
  973. try:
  974. _check(lmdb.mdb_cursor_get(
  975. cur, &key_v, &data_v, lmdb.MDB_FIRST))
  976. except KeyNotFoundError:
  977. ctx[0] = NULL
  978. return
  979. ctx[0] = <Key*>malloc(stat.ms_entries * KLEN)
  980. sz[0] = 0
  981. while True:
  982. ctx[0][sz[0]] = (<Key*>key_v.mv_data)[0]
  983. sz[0] += 1
  984. try:
  985. _check(lmdb.mdb_cursor_get(
  986. cur, &key_v, NULL, lmdb.MDB_NEXT))
  987. except KeyNotFoundError:
  988. break
  989. finally:
  990. self._cur_close(cur)
  991. # Key conversion methods.
  992. cdef inline void lookup_term(self, const Key tk, Buffer* data) except *:
  993. """
  994. look up a term by key.
  995. :param Key key: The key to be looked up.
  996. :param Buffer *data: Buffer structure containing the serialized term.
  997. """
  998. cdef:
  999. lmdb.MDB_val key_v, data_v
  1000. key_v.mv_data = &tk
  1001. key_v.mv_size = KLEN
  1002. _check(
  1003. lmdb.mdb_get(
  1004. self.txn, self.get_dbi('t:st'), &key_v, &data_v
  1005. ),
  1006. f'Error getting data for key \'{tk}\'.'
  1007. )
  1008. data.addr = data_v.mv_data
  1009. data.sz = data_v.mv_size
  1010. cdef object from_key(self, const Key tk):
  1011. """
  1012. Convert a single key into one term.
  1013. :param Key key: The key to be converted.
  1014. """
  1015. cdef Buffer pk_t
  1016. logger.info(f'From key:{tk}')
  1017. self.lookup_term(tk, &pk_t)
  1018. logger.info(f'from_key buffer: {buffer_dump(&pk_t)}')
  1019. # TODO Make Term a class and return that.
  1020. return deserialize_to_rdflib(&pk_t)
  1021. cdef Key to_key(self, term) except? 0:
  1022. """
  1023. Convert a term into a key and insert it in the term key store.
  1024. :param rdflib.Term term: An RDFLib term (URIRef, BNode, Literal).
  1025. :param Key key: Key that will be produced.
  1026. :rtype: void
  1027. """
  1028. cdef:
  1029. lmdb.MDB_txn *_txn
  1030. Hash128 thash
  1031. Buffer pk_t
  1032. Key tk
  1033. #logger.info(f'Serializing term: {term}')
  1034. serialize_from_rdflib(term, &pk_t)
  1035. hash128(&pk_t, &thash)
  1036. key_v.mv_data = thash
  1037. key_v.mv_size = HLEN
  1038. try:
  1039. #logger.debug(
  1040. # f'Check {buffer_dump(&pk_t)} with hash '
  1041. # f'{(<unsigned char*>thash)[:HLEN]} in store before adding.'
  1042. #)
  1043. _check(lmdb.mdb_get(
  1044. self.txn, self.get_dbi(b'th:t'), &key_v, &data_v)
  1045. )
  1046. return (<Key*>data_v.mv_data)[0]
  1047. except KeyNotFoundError:
  1048. #logger.info(f'Adding term {term} to store.')
  1049. # If key is not in the store, add it.
  1050. if self.is_txn_rw:
  1051. # Use existing R/W transaction.
  1052. #logger.info('Working in existing RW transaction.')
  1053. _txn = self.txn
  1054. else:
  1055. # Open new R/W transaction.
  1056. #logger.info('Opening a temporary RW transaction.')
  1057. _check(lmdb.mdb_txn_begin(self.dbenv, NULL, 0, &_txn))
  1058. try:
  1059. # Main entry.
  1060. tk = self._append(&pk_t, b't:st', txn=_txn)
  1061. # Index.
  1062. data_v.mv_data = &tk
  1063. data_v.mv_size = KLEN
  1064. _check(lmdb.mdb_put(
  1065. _txn, self.get_dbi(b'th:t'), &key_v, &data_v, 0
  1066. ))
  1067. if not self.is_txn_rw:
  1068. _check(lmdb.mdb_txn_commit(_txn))
  1069. # Kick the main transaction to see the new terms.
  1070. lmdb.mdb_txn_reset(self.txn)
  1071. _check(lmdb.mdb_txn_renew(self.txn))
  1072. return tk
  1073. except:
  1074. if not self.is_txn_rw:
  1075. lmdb.mdb_txn_abort(_txn)
  1076. raise
  1077. cdef Key _append(
  1078. self, Buffer *value,
  1079. unsigned char *dblabel=b'', lmdb.MDB_txn *txn=NULL,
  1080. unsigned int flags=0
  1081. ) except? 0:
  1082. """
  1083. Append one or more keys and values to the end of a database.
  1084. :param lmdb.Cursor cur: The write cursor to act on.
  1085. :param list(bytes) values: Value(s) to append.
  1086. :rtype: Key
  1087. :return: Key inserted.
  1088. """
  1089. cdef:
  1090. lmdb.MDB_cursor *cur
  1091. Key new_idx
  1092. lmdb.MDB_val key_v, data_v
  1093. if txn is NULL:
  1094. txn = self.txn
  1095. cur = self._cur_open(dblabel, txn=txn)
  1096. try:
  1097. _check(lmdb.mdb_cursor_get(cur, &key_v, NULL, lmdb.MDB_LAST))
  1098. except KeyNotFoundError:
  1099. new_idx = FIRST_KEY
  1100. else:
  1101. new_idx = (<Key*>key_v.mv_data)[0] + 1
  1102. finally:
  1103. self._cur_close(cur)
  1104. key_v.mv_data = &new_idx
  1105. logger.debug(f'New index: {new_idx}')
  1106. logger.debug('Key data inserted: {}'.format((<unsigned char*>key_v.mv_data)[:KLEN]))
  1107. key_v.mv_size = KLEN
  1108. data_v.mv_data = value.addr
  1109. data_v.mv_size = value.sz
  1110. lmdb.mdb_put(
  1111. txn, self.get_dbi(dblabel), &key_v, &data_v,
  1112. flags | lmdb.MDB_APPEND)
  1113. return new_idx
  1114. def _normalize_context(self, context):
  1115. """
  1116. Normalize a context parameter to conform to the model expectations.
  1117. :param context: Context URI or graph.
  1118. :type context: URIRef or Graph or None
  1119. """
  1120. if isinstance(context, rdflib.Graph):
  1121. if context == self or isinstance(
  1122. context.identifier, rdflib.Variable
  1123. ):
  1124. context = None
  1125. else:
  1126. context = context.identifier
  1127. elif isinstance(context, str):
  1128. context = rdflib.URIRef(context)
  1129. return context