lmdb_triplestore.pyx 44 KB

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