lmdb_triplestore.pyx 43 KB

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