Adaptive Framework  0.9.0
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
afw_lmdb_index.c
Go to the documentation of this file.
1 // See the 'COPYING' file in the project root for licensing information.
2 /*
3  * Indexing routines for LMDB Adaptor
4  *
5  * Copyright (c) 2010-2023 Clemson University
6  *
7  */
8 
14 #include "afw.h"
15 #include "afw_uuid.h"
16 #include "afw_adaptor_impl.h"
17 #include "afw_adaptor_impl_index.h"
18 
19 #include "afw_lmdb_index.h"
20 #include "afw_lmdb_internal.h"
22 
23 #define AFW_IMPLEMENTATION_ID "lmdb_index"
25 
26 
27 
28 afw_adaptor_impl_index_t * afw_lmdb_adaptor_impl_index_create(
29  const afw_lmdb_adaptor_session_t * session,
30  const afw_lmdb_adaptor_t * adaptor,
31  MDB_txn * txn,
32  afw_xctx_t * xctx)
33 {
35 
36  /*
37  * You may want to create a new pool for instance, but will just use
38  * xctx's pool in this example.
39  */
41 
42  self->pub.inf = &impl_afw_adaptor_impl_index_inf;
43  self->session = session;
44  self->adaptor = adaptor;
45  self->txn = txn;
46  self->pub.indexDefinitions = impl_afw_adaptor_impl_index_get_index_definitions(
47  (afw_adaptor_impl_index_t*)self, xctx);
48 
49  /* Return new instance. */
50  return (afw_adaptor_impl_index_t*)self;
51 }
52 
53 
54 /*
55  * const afw_utf8_t * afw_lmdb_index_database()
56  *
57  * This routine constructs the database name for locating
58  * a particular index. In general, for LMDB, we use the
59  * format:
60  *
61  * Index#object_type_id#property_name
62  *
63  * For the object_type_id index, we use the special case:
64  *
65  * Index#_meta_#objectType
66  *
67  */
68 const afw_utf8_t * afw_lmdb_index_database(
69  const afw_utf8_t *object_type_id,
70  const afw_utf8_t *key,
71  const afw_pool_t *p,
72  afw_xctx_t *xctx)
73 {
74  const afw_utf8_t *database;
75  const afw_utf8_t separator = AFW_UTF8_LITERAL("#");
76 
77  /* Use the form: Index#object_type_id#key for our index database */
78  if (object_type_id) {
79  database = afw_utf8_concat(p, xctx, &afw_lmdb_s_Index,
80  &separator, object_type_id, &separator, key, NULL);
81  } else {
82  database = afw_utf8_concat(p, xctx, &afw_lmdb_s_Index,
83  &separator, key, NULL);
84  }
85 
86  return database;
87 }
88 
89 
90 /*
91  * Implementation of method release of interface afw_adaptor_impl_index.
92  */
93 void impl_afw_adaptor_impl_index_release(
94  const afw_adaptor_impl_index_t * instance,
95  afw_xctx_t *xctx)
96 {
97 
98 }
99 
100 /*
101  * Implementation of method get_index_definitions of interface afw_adaptor_impl_index.
102  */
103 const afw_object_t *
104 impl_afw_adaptor_impl_index_get_index_definitions (
105  const afw_adaptor_impl_index_t * instance,
106  afw_xctx_t *xctx)
107 {
109  (afw_lmdb_adaptor_impl_index_t *) instance;
110  const afw_adaptor_t *adaptor = (afw_adaptor_t *)self->adaptor;
111  const afw_object_t *indexes;
112 
113  /* lock the adaptor pool to fetch the indexes */
116  self->adaptor->internalConfig, &afw_lmdb_s_indexDefinitions, xctx);
117  if (indexes) {
118  indexes = afw_object_create_clone(indexes,
119  xctx->p, xctx);
120  } else {
121  /* if we don't have one, just create one in our own pool */
122  indexes = afw_object_create_managed(xctx->p, xctx);
123  }
124  }
125  /* unlock, we have our own copy now */
127 
128  return indexes;
129 }
130 
131 /*
132  * Implementation of method update_index_definitions of interface afw_adaptor_impl_index.
133  */
134 void
135 impl_afw_adaptor_impl_index_update_index_definitions (
136  const afw_adaptor_impl_index_t * instance,
137  const afw_object_t * indexDefinitions,
138  afw_xctx_t *xctx)
139 {
141  (afw_lmdb_adaptor_impl_index_t *) instance;
142  const afw_lmdb_adaptor_session_t *session = self->session;
143  const afw_adaptor_t *adaptor = (afw_adaptor_t *)self->session->adaptor;
144  const afw_pool_t *pool;
145  MDB_txn *txn = self->txn;
146 
147  /* clone this object with our adaptor's memory pool */
148  pool = adaptor->p;
149 
150  /* lock the adaptor */
153  session->adaptor->internalConfig, &afw_lmdb_s_indexDefinitions,
155  indexDefinitions, pool, xctx),
156  xctx);
157 
158  /* now write out our new config */
159  afw_lmdb_internal_save_config(session->adaptor,
160  session->adaptor->internalConfig, txn, xctx);
161  }
162 
164 }
165 
166 /*
167  * Implementation of method create of interface afw_adaptor_impl_index.
168  */
169 void
170 impl_afw_adaptor_impl_index_open(
171  const afw_adaptor_impl_index_t * instance,
172  const afw_utf8_t * object_type_id,
173  const afw_utf8_t * key,
174  afw_boolean_t integer,
175  afw_boolean_t unique,
176  afw_boolean_t reverse,
177  const afw_pool_t * pool,
178  afw_xctx_t *xctx)
179 {
181  (afw_lmdb_adaptor_impl_index_t *) instance;
182  const afw_lmdb_adaptor_t *adaptor = self->adaptor;
183  const afw_lmdb_adaptor_session_t * session = NULL;
184  const afw_utf8_t *database;
185  unsigned int flags;
186 
187  database = afw_lmdb_index_database(object_type_id, key, pool, xctx);
188 
189  /* we want to create the database, if it doesn't exist */
190  flags = MDB_CREATE;
191 
192  /* now, select individual properties for our index */
193  if (!unique) flags |= MDB_DUPSORT;
194 
195  if (reverse) flags |= MDB_REVERSEKEY | MDB_REVERSEDUP;
196 
197  if (integer) flags |= MDB_INTEGERKEY;
198 
199  /* open up our exclusive transaction, if we haven't already */
200  if (self->txn == NULL) {
201  AFW_LMDB_BEGIN_TRANSACTION(adaptor, session, 0, true, xctx) {
202 
203  /* now open up the new index database */
204  afw_lmdb_internal_open_database(adaptor,
205  AFW_LMDB_GET_TRANSACTION(),
206  database, flags, pool, xctx);
207 
208  /* and commit our change */
210  }
212  } else {
213  afw_lmdb_internal_open_database(adaptor,
214  self->txn, database, flags, pool, xctx);
215  }
216 }
217 
218 
219 /*
220  * Implementation of method add of interface afw_adaptor_impl_index.
221  */
223  const afw_adaptor_impl_index_t * instance,
224  const afw_utf8_t * object_type_id,
225  const afw_utf8_t * object_id,
226  const afw_utf8_t * indexKey,
227  const afw_utf8_t * value,
228  afw_boolean_t unique,
229  const afw_pool_t * pool,
230  afw_xctx_t *xctx)
231 {
233  (afw_lmdb_adaptor_impl_index_t *) instance;
234  const afw_lmdb_adaptor_session_t *session = self->session;
235  afw_rc_t rc;
236  MDB_txn * txn;
237  MDB_dbi dbi;
238  MDB_val key, data;
239  const afw_uuid_t *uuid;
240  const afw_utf8_t *database;
241  afw_memory_t raw;
242 
243  /* we will get an error if we try to add a key of length 0 */
247  if (value->len == 0) {
248  return 0;
249  }
250 
251  database = afw_lmdb_index_database(
252  object_type_id, indexKey, pool, xctx);
253 
254  /* use the transaction on our session interface */
255  txn = self->txn;
256 
257  dbi = afw_lmdb_internal_open_database(session->adaptor,
258  txn, database, MDB_DUPSORT|MDB_CREATE, pool, xctx);
259 
260  uuid = afw_uuid_from_utf8(object_id, pool, xctx);
261 
262  afw_lmdb_internal_set_key(&raw,
263  object_type_id, uuid, pool, xctx);
264 
265  key.mv_data = (void*)value->s;
266  key.mv_size = value->len;
267 
268  data.mv_data = (void*)raw.ptr;
269  data.mv_size = raw.size;
270 
271  if (unique) {
272  rc = mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE);
273  if (rc != 0) {
274  AFW_THROW_ERROR_RV_Z(general, lmdb, rc,
275  "Unable to add unique index value.", xctx);
276  }
277  } else {
278  rc = mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA);
279  if (rc != 0 && rc != MDB_KEYEXIST) {
280  AFW_THROW_ERROR_RV_Z(general, lmdb, rc,
281  "Unable to add index value.", xctx);
282  }
283  }
284 
285  return rc;
286 }
287 
288 /*
289  * Implementation of method delete of interface afw_adaptor_impl_index.
290  */
292  const afw_adaptor_impl_index_t * instance,
293  const afw_utf8_t *object_type_id,
294  const afw_utf8_t *object_id,
295  const afw_utf8_t *indexKey,
296  const afw_utf8_t *value,
297  const afw_pool_t *pool,
298  afw_xctx_t *xctx)
299 {
301  (afw_lmdb_adaptor_impl_index_t *) instance;
302  const afw_lmdb_adaptor_session_t *session = self->session;
303  MDB_val key, data;
304  MDB_dbi dbi;
305  MDB_txn *txn = self->txn;
306  const afw_utf8_t *database;
307  const afw_uuid_t *uuid;
308  afw_rc_t rc;
309  afw_memory_t raw;
310 
311  /* we will get an error if we try to delete a key of length 0 */
315  if (value->len == 0) {
316  return 0;
317  }
318 
319  database = afw_lmdb_index_database(
320  object_type_id, indexKey, pool, xctx);
321 
322  dbi = afw_lmdb_internal_open_database(session->adaptor,
323  txn, database, MDB_DUPSORT, pool, xctx);
324 
325  uuid = afw_uuid_from_utf8(object_id, pool, xctx);
326 
327  afw_lmdb_internal_set_key(&raw, object_type_id, uuid, pool, xctx);
328 
329  memset(&key, 0, sizeof(MDB_val));
330  key.mv_data = (void *)value->s;
331  key.mv_size = value->len;
332 
333  memset(&data, 0, sizeof(MDB_val));
334  data.mv_data = (void*)raw.ptr;
335  data.mv_size = raw.size;
336 
337  rc = mdb_del(txn, dbi, &key, &data);
338  if (rc != 0 && rc != MDB_NOTFOUND) {
339  AFW_THROW_ERROR_RV_Z(general, lmdb, rc,
340  "Unable to delete index value.", xctx);
341  }
342 
343  return rc;
344 }
345 
346 /*
347  * Implementation of method replace of interface afw_adaptor_impl_index.
348  */
349 afw_rc_t impl_afw_adaptor_impl_index_replace(
350  const afw_adaptor_impl_index_t * instance,
351  const afw_utf8_t *object_type_id,
352  const afw_utf8_t *object_id,
353  const afw_utf8_t *indexKey,
354  const afw_utf8_t *old_value,
355  const afw_utf8_t *new_value,
356  const afw_pool_t *pool,
357  afw_xctx_t *xctx)
358 {
360  (afw_lmdb_adaptor_impl_index_t *) instance;
361  const afw_lmdb_adaptor_session_t *session = self->session;
362  MDB_val key, data;
363  MDB_dbi dbi;
364  MDB_txn *txn = self->txn;
365  const afw_utf8_t *database;
366  const afw_uuid_t *uuid;
367  afw_memory_t raw;
368  afw_rc_t rc;
369 
370  database = afw_lmdb_index_database(
371  object_type_id, indexKey, pool, xctx);
372 
373  dbi = afw_lmdb_internal_open_database(session->adaptor,
374  txn, database, MDB_DUPSORT, pool, xctx);
375 
376  memset(&key, 0, sizeof(MDB_val));
377  key.mv_data = (void *)old_value->s;
378  key.mv_size = old_value->len;
379 
380  memset(&data, 0, sizeof(MDB_val));
381  uuid = afw_uuid_from_utf8(object_id, pool, xctx);
382 
383  afw_lmdb_internal_set_key(&raw, object_type_id, uuid, pool, xctx);
384 
385  data.mv_data = (void*)raw.ptr;
386  data.mv_size = raw.size;
387 
388  /* remove the old value */
389  rc = mdb_del(txn, dbi, &key, &data);
390  if (rc != 0) {
391  AFW_THROW_ERROR_RV_Z(general, lmdb, rc,
392  "Unable to remove old index value.", xctx);
393  }
394 
395  memset(&key, 0, sizeof(MDB_val));
396  key.mv_data = (void *)new_value->s;
397  key.mv_size = new_value->len;
398 
399  memset(&data, 0, sizeof(MDB_val));
400  uuid = afw_uuid_from_utf8(object_id, pool, xctx);
401 
402  afw_lmdb_internal_set_key(&raw, object_type_id, uuid, pool, xctx);
403 
404  data.mv_data = (void*)raw.ptr;
405  data.mv_size = raw.size;
406 
407  /* now write the new value */
408  rc = mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA);
409 
410  return rc;
411 }
412 
413 /*
414  * Implementation of method drop of interface afw_adaptor_impl_index.
415  */
416 afw_rc_t
417 impl_afw_adaptor_impl_index_drop (
418  const afw_adaptor_impl_index_t * instance,
419  const afw_utf8_t * object_type_id,
420  const afw_utf8_t * key,
421  const afw_pool_t * pool,
422  afw_xctx_t * xctx)
423 {
425  (afw_lmdb_adaptor_impl_index_t *) instance;
426  const afw_lmdb_adaptor_session_t *session = self->session;
427  const afw_utf8_t *database;
428  MDB_dbi dbi;
429  MDB_txn *txn = self->txn;
430  afw_rc_t rc;
431 
432  database = afw_lmdb_index_database(
433  object_type_id, key, pool, xctx);
434 
435  dbi = afw_lmdb_internal_open_database(session->adaptor,
436  txn, database, MDB_DUPSORT|MDB_CREATE, pool, xctx);
437 
438  /* (1) means delete it from the environment and close the DB handle */
439  rc = mdb_drop(txn, dbi, 1);
440 
441  return rc;
442 }
443 
444 /*
445  * Implementation of method open_cursor of interface afw_adaptor_impl_index.
446  */
448 impl_afw_adaptor_impl_index_open_cursor (
449  const afw_adaptor_impl_index_t * instance,
450  const afw_utf8_t * object_type_id,
451  const afw_utf8_t * key,
452  int operator,
453  const afw_utf8_t * value,
454  afw_boolean_t unique,
455  const afw_pool_t * pool,
456  afw_xctx_t * xctx)
457 {
459  (afw_lmdb_adaptor_impl_index_t *) instance;
461  const afw_utf8_t * database;
462 
463  database = afw_lmdb_index_database(
464  object_type_id, key, pool, xctx);
465 
466  cursor = afw_lmdb_internal_cursor_create(self->session,
467  database, object_type_id, value, operator, unique, xctx);
468 
469  return cursor;
470 }
471 
472 /*
473  * Implementation of method get_session of interface afw_adaptor_impl_index.
474  */
475 const afw_adaptor_session_t *
476 impl_afw_adaptor_impl_index_get_session (
477  const afw_adaptor_impl_index_t * instance,
478  afw_xctx_t *xctx)
479 {
480  /* Assign instance pointer to self. */
483 
484  return (const afw_adaptor_session_t *) self->session;
485 }
Adaptive Framework Core API.
Helpers for adaptor implementation development.
Helpers for afw_adaptor implementation index development.
Interface afw_interface implementation declares.
Adaptive Framework register generated (afw_lmdb) header.
Helpers for afw_adaptor implementation index development.
Adaptive Framework LMDB Adaptor Internal Header.
#define AFW_LMDB_END_TRANSACTION()
End an LMDB transaction.
#define AFW_LMDB_BEGIN_TRANSACTION(adaptor, session, flags, exclusive, xctx)
Begin an LMDB transaction.
#define AFW_LMDB_COMMIT_TRANSACTION()
Commit a transaction.
Adaptive Framework UUID header.
afw_rc_t impl_afw_adaptor_impl_index_delete(const afw_adaptor_impl_index_t *instance, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, const afw_utf8_t *indexKey, const afw_utf8_t *value, const afw_pool_t *pool, afw_xctx_t *xctx)
afw_rc_t impl_afw_adaptor_impl_index_add(const afw_adaptor_impl_index_t *instance, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, const afw_utf8_t *indexKey, const afw_utf8_t *value, afw_boolean_t unique, const afw_pool_t *pool, afw_xctx_t *xctx)
#define AFW_ADAPTOR_IMPL_LOCK_WRITE_END
Macro to end an adaptor write lock section.
#define AFW_ADAPTOR_IMPL_LOCK_READ_END
Macro to end an adaptor read lock section.
#define AFW_ADAPTOR_IMPL_LOCK_READ_BEGIN(adaptor)
Macro to begin an adaptor read lock section.
#define AFW_ADAPTOR_IMPL_LOCK_WRITE_BEGIN(adaptor)
Macro to begin an adaptor write lock section.
afw_object_set_property_as_object(const afw_object_t *object, const afw_utf8_t *property_name, const afw_object_t *internal, afw_xctx_t *xctx)
Set property function for data type object values.
#define afw_object_old_get_property_as_object(object, property_name, xctx)
Get property function for data type object value.
#define AFW_UTF8_LITERAL(A_STRING)
String literal initializer.
Definition: afw_common.h:582
_Bool afw_boolean_t
Definition: afw_common.h:373
int afw_rc_t
Definition: afw_common.h:663
#define AFW_THROW_ERROR_RV_Z(code, rv_source_id, rv, message_z, xctx)
Macro used to set error and rv in xctx and throw it.
Definition: afw_error.h:301
afw_object_create_clone(const afw_object_t *object, const afw_pool_t *p, afw_xctx_t *xctx)
Clone an object to a specified pool.
Definition: afw_memory.c:138
#define afw_object_create_managed(p, xctx)
Create an empty entity object in its own pool.
Definition: afw_object.h:913
const afw_utf8_t * afw_utf8_concat(const afw_pool_t *p, afw_xctx_t *xctx,...)
Concatenate strings with result in specifed pool.
afw_uuid_from_utf8(const afw_utf8_t *s, const afw_pool_t *p, afw_xctx_t *xctx)
Convert standard format UUID utf-8 string to uuid.
Definition: afw_uuid.c:117
#define afw_xctx_calloc_type(type, xctx)
Macro to allocate cleared memory to hold type in xctx's pool.
Definition: afw_xctx.h:199
Interface afw_adaptor_impl_index_cursor public struct.
Interface afw_adaptor_impl_index public struct.
Interface afw_adaptor public struct.
Interface afw_adaptor_session public struct.
Struct for memory pointer and size.
Definition: afw_common.h:505
Interface afw_object public struct.
Interface afw_pool public struct.
NFC normalized UTF-8 string.
Definition: afw_common.h:545
Interface afw_xctx public struct.