Adaptive Framework  0.9.0
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
afw_lmdb_internal.c
1 // See the 'COPYING' file in the project root for licensing information.
2 /*
3  * Adaptive Framework LMDB Adaptor Internal
4  *
5  * Copyright (c) 2010-2023 Clemson University
6  *
7  */
8 
9 #include "afw.h"
10 #include "afw_uuid.h"
12 #include "afw_lmdb_internal.h"
13 
14 #define AFW_IMPLEMENTATION_ID "lmdb"
18 
19 /*
20  * Functions for maintaining a LMDB Database handle.
21  * Both the environment and dbi pointers are required
22  * to close it and release its resources, so we
23  * group them together. These routines allow us to
24  * register the handle on the xctx for release.
25  */
26 afw_lmdb_dbi_t * afw_lmdb_internal_dbi_handle(
27  MDB_env * env,
28  MDB_dbi dbi,
29  const afw_pool_t * pool,
30  afw_xctx_t * xctx)
31 {
32  afw_lmdb_dbi_t *dbi_p;
33 
34  dbi_p = afw_pool_calloc_type(pool, afw_lmdb_dbi_t, xctx);
35  dbi_p->env = env;
36  dbi_p->dbi = dbi;
37 
38  return dbi_p;
39 }
40 
41 afw_rc_t afw_lmdb_internal_close_database(void *val)
42 {
43  afw_lmdb_dbi_t *dbi_p = (afw_lmdb_dbi_t *)val;
44 
45  mdb_dbi_close(dbi_p->env, dbi_p->dbi);
46 
47  return 0;
48 }
49 
50 /*
51  * MDB_dbi afw_lmdb_internal_open_database()
52  *
53  * This routine opens a database by first trying the pre-loaded
54  * handles that were created at adaptor create. If it's not found,
55  * then it creates one, on-the-fly, and registers a cleanup to close
56  * it.
57  *
58  * Note: only one transaction at a time may open a new database.
59  * Therefore, the AFW_LMDB_BEGIN_TRANSACTION() macro requires an exclusive
60  * writer lock, ahead of time, to achieve this.
61  */
62 MDB_dbi afw_lmdb_internal_open_database(
63  const afw_lmdb_adaptor_t * adaptor,
64  MDB_txn * txn,
65  const afw_utf8_t * database,
66  unsigned int flags,
67  const afw_pool_t * p,
68  afw_xctx_t * xctx)
69 {
70  const afw_pool_t *adaptor_p;
71  MDB_dbi dbi = 0;
72  afw_lmdb_dbi_t *dbi_p;
73  int rc;
74 
75  adaptor_p = ((afw_adaptor_t *)adaptor)->p;
76 
77  /* first check our adaptor's dbi_handles */
78  dbi_p = apr_hash_get(adaptor->dbi_handles, database->s, database->len);
79 
80  /* if we got a database handle, use it */
81  if (dbi_p) {
82  return dbi_p->dbi;
83  }
84 
85  /* if it's not found from our pre-loaded databases, then try to open it */
86  rc = mdb_dbi_open(txn, afw_utf8_to_utf8_z(database, p, xctx), flags, &dbi);
87  if (rc == 0) {
88  dbi_p = afw_lmdb_internal_dbi_handle(
89  adaptor->dbEnv, dbi, adaptor_p, xctx);
90 
91  /* add it to our handle list, so we can access it later */
92  apr_hash_set(adaptor->dbi_handles, database->s,
93  database->len, dbi_p);
94  } else if (rc == MDB_NOTFOUND) {
95  AFW_THROW_ERROR_RV_FZ(not_found, lmdb, rc, xctx,
96  "Unable to open database: %" AFW_UTF8_FMT ".",
97  AFW_UTF8_FMT_ARG(database));
98  } else {
99  AFW_THROW_ERROR_RV_FZ(general, lmdb, rc, xctx,
100  "Unable to open database: %" AFW_UTF8_FMT ".",
101  AFW_UTF8_FMT_ARG(database));
102  }
103 
104  return dbi;
105 }
106 
107 /*
108  * Functions for opening and automatically releasing
109  * LMDB cursors by an xctx cleanup registration.
110  */
111 afw_rc_t afw_lmdb_internal_close_cursor(void *cursor)
112 {
113  mdb_cursor_close((MDB_cursor*)cursor);
114 
115  return 0;
116 }
117 
118 MDB_cursor * afw_lmdb_internal_open_cursor(
119  const afw_lmdb_adaptor_session_t *session,
120  MDB_dbi dbi,
121  afw_xctx_t *xctx)
122 {
123  int rc;
124  MDB_cursor *cursor = NULL;
125  MDB_txn *txn = session->currTxn;
126 
127  rc = mdb_cursor_open(txn, dbi, &cursor);
128  if (rc == MDB_NOTFOUND) {
129  AFW_THROW_ERROR_RV_Z(not_found, lmdb, rc,
130  "Cursor not found.", xctx);
131  } else if (rc) {
132  AFW_THROW_ERROR_RV_Z(general, lmdb, rc,
133  "Unable to open cursor.", xctx);
134  }
135 
136  return cursor;
137 }
138 
139 /*
140  * Sets the raw key in the format of:
141  *
142  * {object_type_id}{uuid}
143  */
144 void afw_lmdb_internal_set_key(
145  afw_memory_t * key,
146  const afw_utf8_t * object_type_id,
147  const afw_uuid_t * uuid,
148  const afw_pool_t * p,
149  afw_xctx_t *xctx)
150 {
151  key->ptr = afw_pool_malloc(p, object_type_id->len + sizeof(afw_uuid_t), xctx);
152  if (object_type_id->len)
153  memcpy((char*)(key->ptr), object_type_id->s, object_type_id->len);
154  memcpy((char*)(key->ptr) + object_type_id->len, uuid, sizeof(afw_uuid_t));
155  key->size = object_type_id->len + sizeof(afw_uuid_t);
156 }
157 
158 /*
159  * Parses the raw key in the format of:
160  *
161  * {ObjectType}{uuid}
162  *
163  * And returns the object_type_id and uuid.
164  */
165 void afw_lmdb_internal_get_key(
166  afw_memory_t * key,
167  afw_utf8_t * object_type_id,
168  afw_uuid_t * uuid)
169 {
170  if (key->size > sizeof(afw_uuid_t)) {
171  object_type_id->s = (const afw_utf8_octet_t *)key->ptr;
172  object_type_id->len = key->size - sizeof(afw_uuid_t);
173  memcpy(uuid, key->ptr + object_type_id->len, sizeof(afw_uuid_t));
174  } else {
175  object_type_id->len = 0;
176  memcpy(uuid, key->ptr, sizeof(afw_uuid_t));
177  }
178 }
179 
180 /*
181  * afw_rc_t afw_lmdb_internal_create_entry()
182  *
183  * The foundational routine for storing a key/value entry
184  * into LMDB. All other methods/interfaces should handle
185  * which database and transaction to use, along with how
186  * to serialize the data into a raw key and value.
187  *
188  * Unlike replace, this routine will not overwrite an existing
189  * key/value.
190  */
191 afw_rc_t afw_lmdb_internal_create_entry(
192  MDB_txn *txn,
193  MDB_dbi dbi,
194  const afw_memory_t * raw_key,
195  const afw_memory_t * raw_value,
196  afw_xctx_t *xctx)
197 {
198  MDB_val key, value;
199  int rc;
200 
201  key.mv_data = (void*)raw_key->ptr;
202  key.mv_size = raw_key->size;
203 
204  value.mv_data = (void*)raw_value->ptr;
205  value.mv_size = raw_value->size;
206 
207  rc = mdb_put(txn, dbi, &key, &value, MDB_NOOVERWRITE);
208 
209  return rc;
210 }
211 
212 /*
213  * afw_rc_t afw_lmdb_internal_replace_entry()
214  *
215  * The foundational routine for replacing a key/value entry
216  * into LMDB. All other methods/interfaces should handle
217  * which database and transaction to use, along with how
218  * to serialize the data into a raw key and value.
219  */
220 afw_rc_t afw_lmdb_internal_replace_entry(
221  MDB_txn *txn,
222  MDB_dbi dbi,
223  const afw_memory_t * raw_key,
224  const afw_memory_t * raw_value,
225  afw_xctx_t *xctx)
226 {
227  MDB_val key, value;
228  int rc;
229 
230  key.mv_data = (void*)raw_key->ptr;
231  key.mv_size = raw_key->size;
232 
233  value.mv_data = (void*)raw_value->ptr;
234  value.mv_size = raw_value->size;
235 
236  rc = mdb_put(txn, dbi, &key, &value, 0);
237 
238  return rc;
239 }
240 
241 /*
242  * afw_rc_t afw_lmdb_internal_delete_entry()
243  *
244  * The foundational routine for deleting a key/value entry
245  * into LMDB. All other methods/interfaces should handle
246  * which database and transaction to use, along with how
247  * to serialize the data into a raw key and value.
248  */
249 afw_rc_t afw_lmdb_internal_delete_entry(
250  MDB_txn *txn,
251  MDB_dbi dbi,
252  const afw_memory_t * raw_key,
253  const afw_memory_t * raw_value,
254  afw_xctx_t *xctx)
255 {
256  MDB_val key, value;
257  int rc;
258 
259  key.mv_data = (void*)raw_key->ptr;
260  key.mv_size = raw_key->size;
261 
262  if (raw_value) {
263  value.mv_data = (void*)raw_value->ptr;
264  value.mv_size = raw_value->size;
265  } else {
266  value.mv_data = NULL;
267  value.mv_size = 0;
268  }
269 
270  rc = mdb_del(txn, dbi, &key, &value);
271 
272  return rc;
273 }
274 
275 /*
276  * afw_rc_t afw_lmdb_internal_get_entry()
277  *
278  * The foundational routine for getting a key/value entry
279  * from LMDB. All other methods/interfaces should handle
280  * which database and transaction to use, along with how
281  * to serialize the data into a raw key.
282  */
283 afw_rc_t afw_lmdb_internal_get_entry(
284  MDB_txn *txn,
285  MDB_dbi dbi,
286  const afw_memory_t * raw_key,
287  afw_memory_t * raw_value,
288  afw_xctx_t *xctx)
289 {
290  MDB_val key, value;
291  int rc;
292 
293  key.mv_data = (void*)raw_key->ptr;
294  key.mv_size = raw_key->size;
295 
296  memset(&value, 0, sizeof(MDB_val));
297 
298  rc = mdb_get(txn, dbi, &key, &value);
299  if (rc == 0) {
300  raw_value->ptr = value.mv_data;
301  raw_value->size = value.mv_size;
302  }
303 
304  return rc;
305 }
306 
307 
308 /*
309  * void afw_lmdb_internal_create_entry_from_object()
310  *
311  * This routine takes an Adaptive Object and adds it to
312  * the database specified with dbi.
313  *
314  * The object_id becomes the key, stored as a binary UUID.
315  * The object is converted into a string format, specified
316  * by the configured adaptor content-type.
317  *
318  */
319 void afw_lmdb_internal_create_entry_from_object(
320  const afw_lmdb_adaptor_session_t *self,
321  const afw_utf8_t *object_type_id,
322  const afw_utf8_t *object_id,
323  const afw_object_t *object,
324  MDB_dbi dbi,
325  afw_xctx_t *xctx)
326 {
327  afw_memory_t key, value;
328  const afw_uuid_t *uuid;
329  const afw_memory_t *object_string;
330  int rc;
331  MDB_txn *txn = self->currTxn;
332 
333  uuid = afw_uuid_from_utf8(object_id, xctx->p, xctx);
334  if (uuid == NULL)
335  AFW_THROW_ERROR_Z(bad_request,
336  "Invalid object_id format (UUID required).", xctx);
337 
338  object_string = afw_content_type_object_to_raw(
339  self->adaptor->ubjson, object, &afw_object_options_essential,
340  xctx->p, xctx);
341 
342  afw_lmdb_internal_set_key(&key,
343  object_type_id, uuid, xctx->p, xctx);
344 
345  value.ptr = object_string->ptr;
346  value.size = object_string->size;
347 
348  rc = afw_lmdb_internal_create_entry(
349  txn, dbi, &key, &value, xctx);
350  if (rc)
351  AFW_THROW_ERROR_RV_Z(general, lmdb, rc,
352  "Error writing object to database.", xctx);
353 }
354 
355 /*
356  * void afw_lmdb_internal_replace_entry_from_object()
357  *
358  * This routine takes an Adaptive Object and replaces it in
359  * the database specified with dbi.
360  *
361  * The object_id becomes the key, stored as a binary UUID.
362  * The object is converted into a string format, specified
363  * by the configured adaptor content-type.
364  *
365  * Note: if one does not already exist, this will create a
366  * new entry.
367  */
368 void afw_lmdb_internal_replace_entry_from_object(
369  const afw_lmdb_adaptor_session_t *self,
370  const afw_utf8_t *object_type_id,
371  const afw_utf8_t *object_id,
372  const afw_object_t *object,
373  MDB_dbi dbi,
374  afw_xctx_t *xctx)
375 {
376  afw_memory_t key, value;
377  const afw_uuid_t *uuid;
378  const afw_memory_t *object_string;
379  MDB_txn *txn = self->currTxn;
380  int rc;
381 
382  uuid = afw_uuid_from_utf8(object_id, xctx->p, xctx);
383  if (uuid == NULL)
384  AFW_THROW_ERROR_Z(bad_request,
385  "Invalid object_id format (UUID required).", xctx);
386 
387  object_string = afw_content_type_object_to_raw(
388  self->adaptor->ubjson, object, &afw_object_options_essential,
389  xctx->p, xctx);
390 
391  afw_lmdb_internal_set_key(&key,
392  object_type_id, uuid, xctx->p, xctx);
393 
394  value.ptr = object_string->ptr;
395  value.size = object_string->size;
396 
397  rc = afw_lmdb_internal_replace_entry(
398  txn, dbi, &key, &value, xctx);
399  if (rc)
400  AFW_THROW_ERROR_RV_Z(general, lmdb, rc,
401  "Error writing object to database.", xctx);
402 }
403 
404 /*
405  * const afw_object_t * afw_lmdb_internal_create_object_from_entry()
406  *
407  * This routine looks fetches the object from the specified
408  * database and returns it as an Adaptive object.
409  *
410  */
411 const afw_object_t * afw_lmdb_internal_create_object_from_entry(
412  const afw_lmdb_adaptor_session_t *self,
413  const afw_utf8_t *object_type_id,
414  const afw_utf8_t *object_id,
415  MDB_dbi dbi,
416  afw_xctx_t *xctx)
417 {
418  const afw_value_t *value;
419 
420  value = afw_lmdb_internal_create_value_from_entry(self,
421  object_type_id, object_id, dbi, xctx);
422 
423  return afw_value_as_object(value, xctx);
424 }
425 
426 /*
427  * const afw_value_t * afw_lmdb_internal_create_value_from_entry()
428  *
429  * This routine looks fetches the object from the specified
430  * database and returns it as an Adaptive Value.
431  *
432  */
433 const afw_value_t * afw_lmdb_internal_create_value_from_entry(
434  const afw_lmdb_adaptor_session_t *self,
435  const afw_utf8_t *object_type_id,
436  const afw_utf8_t *object_id,
437  MDB_dbi dbi,
438  afw_xctx_t *xctx)
439 {
440  afw_lmdb_adaptor_t *adaptor = (afw_lmdb_adaptor_t *)self->adaptor;
441  const afw_object_t *object;
442  const afw_value_t *v;
443  afw_memory_t raw;
444  afw_memory_t key;
445  afw_memory_t value;
446  const afw_uuid_t *uuid;
447  MDB_txn *txn = self->currTxn;
448 
449  uuid = afw_uuid_from_utf8(object_id, xctx->p, xctx);
450 
451  afw_lmdb_internal_set_key(&key,
452  object_type_id, uuid, xctx->p, xctx);
453  if (afw_lmdb_internal_get_entry(
454  txn, dbi, &key, &value, xctx) != 0)
455  {
456  /* no entry for this key */
457  AFW_THROW_ERROR_Z(general,
458  "Unable to locate entry in database.", xctx);
459  }
460 
461  /* convert the data into a value */
462  raw.size = value.size;
463  raw.ptr = value.ptr;
464 
466  adaptor->ubjson, &raw, NULL, xctx->p, xctx);
467 
468  object = afw_value_as_object(v, xctx);
469 
470  afw_object_meta_set_ids(object, &adaptor->pub.adaptor_id,
471  object_type_id, object_id, xctx);
472 
473  return v;
474 }
475 
476 /*
477  * void afw_lmdb_internal_save_config()
478  *
479  * This routine writes the config object out to
480  * the internal configuration location. We let
481  * the LMDB transaction mechanisms provide our
482  * locking for us.
483  *
484  */
485 void afw_lmdb_internal_save_config(
486  afw_lmdb_adaptor_t *self,
487  const afw_object_t *config,
488  MDB_txn *txn,
489  afw_xctx_t *xctx)
490 {
491  MDB_dbi dbi;
492  MDB_val key, data;
493  afw_uuid_t uuid;
494  const afw_memory_t *raw;
495  const afw_value_t *now;
496  int rc;
497 
498  /* the key for configuration is found at UUID(0) */
499  memset(&uuid, 0, sizeof(afw_uuid_t));
500 
501  dbi = afw_lmdb_internal_open_database(self,
502  txn, &afw_lmdb_s_Primary, MDB_CREATE, xctx->p, xctx);
503 
504  /* set our last updateTimeStamp property */
505  now = afw_value_create_dateTime_now_utc(config->p, xctx);
506  afw_object_set_property(config, &afw_lmdb_s_updateTimeStamp, now, xctx);
507 
508  /* write out our object to raw string */
509  raw = afw_content_type_object_to_raw(self->ubjson, config,
511  config->p, xctx);
512 
513  key.mv_data = (void *)&uuid;
514  key.mv_size = sizeof(afw_uuid_t);
515  data.mv_data = (void *)raw->ptr;
516  data.mv_size = raw->size;
517 
518  rc = mdb_put(txn, dbi, &key, &data, 0);
519  if (rc) {
520  AFW_THROW_ERROR_RV_Z(general, lmdb, rc,
521  "Unable to write internal configuration.", xctx);
522  }
523 }
524 
525 /*
526  * const afw_object_t * afw_lmdb_internal_get_config()
527  *
528  * This routine reads the internal configuration object.
529  * The config object is stored at object_id (0) in our
530  * primary database.
531  *
532  */
533 const afw_object_t * afw_lmdb_internal_get_config(
534  afw_lmdb_adaptor_t *self,
535  MDB_txn *txn,
536  const afw_pool_t *p,
537  afw_xctx_t *xctx)
538 {
539  const afw_object_t *config = NULL;
540  const afw_utf8_t *object_type_id, *object_id;
541  const afw_value_t *value;
542  const afw_value_t *now;
543  afw_uuid_t uuid;
544  MDB_dbi dbi;
545  MDB_val key, data;
546  afw_memory_t raw;
547  const afw_memory_t *raw_out;
548  int rc;
549 
550  /* the key for configuration is found at UUID(0) */
551  memset(&uuid, 0, sizeof(afw_uuid_t));
552  object_id = afw_uuid_to_utf8(&uuid, p, xctx);
553 
554  /* set our object type to an internal type */
555  /* (FIXME: inherit from config object types?) */
556  object_type_id = &afw_lmdb_s_internalConfig;
557 
558  dbi = afw_lmdb_internal_open_database(self, txn,
559  &afw_lmdb_s_Primary, MDB_CREATE, p, xctx);
560 
561  key.mv_data = (void *)&uuid;
562  key.mv_size = sizeof(afw_uuid_t);
563  memset(&data, 0, sizeof(MDB_val));
564 
565  rc = mdb_get(txn, dbi, &key, &data);
566  if (rc == MDB_NOTFOUND) {
567  /* first time running? create a new internal config */
568  config = afw_object_create_managed(p, xctx);
569 
570  /* get the current time for timestamps */
571  now = afw_value_create_dateTime_now_utc(p, xctx);
572 
573  afw_object_set_property(config, &afw_lmdb_s_createTimeStamp, now, xctx);
574  afw_object_set_property(config, &afw_lmdb_s_updateTimeStamp, now, xctx);
575 
576  /* go ahead and write it back out */
578  self->ubjson, config, &afw_object_options_essential,
579  p, xctx);
580 
581  key.mv_data = (void *)&uuid;
582  key.mv_size = sizeof(afw_uuid_t);
583  data.mv_data = (void *)raw_out->ptr;
584  data.mv_size = raw_out->size;
585 
586  rc = mdb_put(txn, dbi, &key, &data, 0);
587  if (rc) {
588  AFW_THROW_ERROR_RV_Z(general, lmdb, rc,
589  "Unable to write internal configuration.", xctx);
590  }
591  } else if (rc == 0) {
592  /* found it, now parse and load it */
593  raw.ptr = data.mv_data;
594  raw.size = data.mv_size;
595 
597  self->ubjson, &raw, NULL, p, xctx);
598 
599  config = afw_value_as_object(value, xctx);
600  afw_object_meta_set_ids(config, &self->pub.adaptor_id, object_type_id,
601  object_id, xctx);
602  } else {
603  /* some other problem! */
604  AFW_THROW_ERROR_RV_Z(general, lmdb, rc,
605  "Unable to find internal configuration in the primary database.", xctx);
606  }
607 
608  return config;
609 }
610 
611 /*
612  * Implementation of Key Value interface
613  *
614  * The following routines implement the interface for
615  * managing key/values in LMDB.
616  */
619  afw_xctx_t *xctx)
620 {
621  afw_lmdb_key_value_t *self;
622 
624  self->pub.inf = &impl_afw_adaptor_key_value_inf;
625 
626  self->session = (afw_adaptor_session_t *)session;
627 
628  return self;
629 }
630 
631 /*
632  * Implementation of method add of interface afw_adaptor_key_value.
633  */
634 void
636  const afw_adaptor_key_value_t * instance,
637  const afw_utf8_t * namespace,
638  const afw_memory_t * key,
639  const afw_memory_t * value,
640  afw_xctx_t *xctx)
641 {
642  /* Assign instance pointer to self. */
643  afw_lmdb_key_value_t * self =
644  (afw_lmdb_key_value_t *)instance;
645  afw_lmdb_adaptor_session_t * session =
646  (afw_lmdb_adaptor_session_t *)self->session;
647  const afw_utf8_t separator = AFW_UTF8_LITERAL("#");
648  const afw_utf8_t *database;
649  MDB_dbi dbi;
650  MDB_txn *txn;
651  afw_rc_t rc;
652 
653  AFW_LMDB_BEGIN_TRANSACTION(session->adaptor, session, 0, false, xctx) {
654 
655  txn = AFW_LMDB_GET_TRANSACTION();
656 
657  database = afw_utf8_concat(xctx->p, xctx, &afw_lmdb_s_Extern,
658  &separator, namespace, NULL);
659 
661  dbi = afw_lmdb_internal_open_database(session->adaptor,
662  txn, database, MDB_CREATE, xctx->p, xctx);
663 
664  rc = afw_lmdb_internal_create_entry(
665  txn, dbi, key, value, xctx);
666  if (rc) {
667  AFW_THROW_ERROR_RV_Z(general, lmdb_internal, rc,
668  "Unable to write key/value to database.", xctx);
669  }
670 
672  }
674 }
675 
676 /*
677  * Implementation of method delete of interface afw_adaptor_key_value.
678  */
679 void
680 impl_afw_adaptor_key_value_delete (
681  const afw_adaptor_key_value_t * instance,
682  const afw_utf8_t * namespace,
683  const afw_memory_t * key,
684  const afw_memory_t * value,
685  afw_boolean_t must_exist,
686  afw_xctx_t *xctx)
687 {
688  /* Assign instance pointer to self. */
689  afw_lmdb_key_value_t * self =
690  (afw_lmdb_key_value_t *)instance;
691  afw_lmdb_adaptor_session_t * session =
692  (afw_lmdb_adaptor_session_t *)self->session;
693  const afw_utf8_t separator = AFW_UTF8_LITERAL("#");
694  const afw_utf8_t *database;
695  MDB_dbi dbi;
696  MDB_txn *txn;
697  afw_rc_t rc;
698 
699  AFW_LMDB_BEGIN_TRANSACTION(session->adaptor, session, 0, false, xctx) {
700 
701  txn = AFW_LMDB_GET_TRANSACTION();
702 
703  database = afw_utf8_concat(xctx->p, xctx, &afw_lmdb_s_Extern,
704  &separator, namespace, NULL);
705 
706  dbi = afw_lmdb_internal_open_database(session->adaptor,
707  txn, database, 0, xctx->p, xctx);
708 
709  rc = afw_lmdb_internal_delete_entry(
710  txn, dbi, key, value, xctx);
711  if (rc == MDB_NOTFOUND) {
712  if (must_exist)
713  AFW_THROW_ERROR_RV_Z(general, lmdb_internal, dbi,
714  "Unable to delete key/value.", xctx);
715  else
716  return;
717  } else if (rc) {
718  AFW_THROW_ERROR_RV_Z(general, lmdb_internal, rc,
719  "Unable to delete key/value to database.", xctx);
720  }
721 
723  }
725 }
726 
727 /*
728  * Implementation of method replace of interface afw_adaptor_key_value.
729  */
730 void
731 impl_afw_adaptor_key_value_replace (
732  const afw_adaptor_key_value_t * instance,
733  const afw_utf8_t * namespace,
734  const afw_memory_t * key,
735  const afw_memory_t * value,
736  afw_boolean_t must_exist,
737  afw_xctx_t *xctx)
738 {
739  /* Assign instance pointer to self. */
740  afw_lmdb_key_value_t * self =
741  (afw_lmdb_key_value_t *)instance;
742  afw_lmdb_adaptor_session_t * session =
743  (afw_lmdb_adaptor_session_t *)self->session;
744  const afw_utf8_t separator = AFW_UTF8_LITERAL("#");
745  const afw_utf8_t *database;
746  MDB_dbi dbi;
747  MDB_txn *txn;
748  afw_rc_t rc;
749  afw_memory_t existing;
750 
751  AFW_LMDB_BEGIN_TRANSACTION(session->adaptor, session, 0, false, xctx) {
752 
753  txn = AFW_LMDB_GET_TRANSACTION();
754 
755  database = afw_utf8_concat(xctx->p, xctx, &afw_lmdb_s_Extern,
756  &separator, namespace, NULL);
757 
758  if (must_exist)
759  dbi = afw_lmdb_internal_open_database(session->adaptor,
760  txn, database, 0, xctx->p, xctx);
761  else
762  dbi = afw_lmdb_internal_open_database(session->adaptor,
763  txn, database, MDB_CREATE, xctx->p, xctx);
764 
765  if (must_exist) {
766  rc = afw_lmdb_internal_get_entry(
767  txn, dbi, key, &existing, xctx);
768  if (rc != 0 ||
769  (value->size != existing.size) ||
770  (memcmp(value->ptr, existing.ptr, value->size) != 0))
771  {
772  AFW_THROW_ERROR_RV_Z(general, lmdb_internal, dbi,
773  "Existing key/value does not exist for replacement.", xctx);
774  }
775  }
776 
777  rc = afw_lmdb_internal_replace_entry(
778  txn, dbi, key, value, xctx);
779  if (rc == MDB_NOTFOUND) {
780  AFW_THROW_ERROR_RV_Z(not_found, lmdb, rc,
781  "Entry not found.", xctx);
782  } else if (rc) {
783  AFW_THROW_ERROR_RV_Z(general, lmdb_internal, rc,
784  "Unable to write key/value to database.", xctx);
785  }
786 
788  }
790 }
791 
792 
793 /*
794  * Implementation of method get of interface afw_adaptor_key_value.
795  */
796 const afw_memory_t *
798  const afw_adaptor_key_value_t * instance,
799  const afw_utf8_t * namespace,
800  const afw_memory_t * key,
801  afw_xctx_t *xctx)
802 {
803  afw_lmdb_key_value_t * self =
804  (afw_lmdb_key_value_t *)instance;
805  afw_lmdb_adaptor_session_t * session =
806  (afw_lmdb_adaptor_session_t *)self->session;
807  const afw_utf8_t separator = AFW_UTF8_LITERAL("#");
808  const afw_utf8_t *database;
809  MDB_dbi dbi;
810  MDB_txn *txn;
811  afw_memory_t existing;
812  const afw_memory_t *value;
813 
814  AFW_LMDB_BEGIN_TRANSACTION(session->adaptor, session, 0, false, xctx) {
815 
816  txn = AFW_LMDB_GET_TRANSACTION();
817 
818  database = afw_utf8_concat(xctx->p, xctx, &afw_lmdb_s_Extern,
819  &separator, namespace, NULL);
820 
821  dbi = afw_lmdb_internal_open_database(session->adaptor,
822  txn, database, 0, xctx->p, xctx);
823 
824  afw_lmdb_internal_get_entry(
825  txn, dbi, key, &existing, xctx);
826 
828  }
830 
831  /* allocate a new afw_memory_t * to return */
832  //value->size = existing.size;
833  //value->ptr = afw_xctx_calloc(existing.size, xctx);
834  //memcpy(value->ptr, existing.ptr, value->size);return NULL;
836  return value;
837 }
838 
839 
840 /*
841  * Implementation of Index Cursor interface
842  *
843  * The following routines implement the interface for
844  * opening a cursor against an indexed property.
845  */
846 void afw_lmdb_internal_cursor_reset(
848  afw_xctx_t *xctx)
849 {
850  const afw_utf8_t *key_string;AFW_POSSIBLY_UNUSED_VARIABLE int rc;
852 
853  memset(&self->data, 0, sizeof(MDB_val));
854  if (self->key_string) {
855  key_string = afw_utf8_create_copy(
856  self->key_string->s, self->key_string->len,
857  xctx->p, xctx);
858 
859  self->key.mv_data = (void *)key_string->s;
860  self->key.mv_size = key_string->len;
861  } else {
862  self->key.mv_data = NULL;
863  self->key.mv_size = 0;
864  }
865 
866  /* the filter entry operator helps us determine our start position */
867  switch (self->operator) {
868  case afw_query_criteria_filter_op_id_ne:
869  rc = mdb_cursor_get(self->cursor, &self->key,
870  &self->data, MDB_FIRST);
871  break;
872 
873  case afw_query_criteria_filter_op_id_eq:
874  rc = mdb_cursor_get(self->cursor, &self->key,
875  &self->data, MDB_SET);
876  break;
877 
878  case afw_query_criteria_filter_op_id_ge:
879  rc = mdb_cursor_get(self->cursor, &self->key,
880  &self->data, MDB_SET_RANGE);
881  break;
882 
883  case afw_query_criteria_filter_op_id_le:
884  rc = mdb_cursor_get(self->cursor, &self->key,
885  &self->data, MDB_SET_RANGE);
886 
887  /* MDB_SET_RANGE may have gone past our key */
888  if (memcmp(self->key.mv_data, self->key_string->s,
889  self->key_string->len) > 0) {
890  /* So set it to our previous data item */
891  rc = mdb_cursor_get(self->cursor, &self->key,
892  &self->data, MDB_PREV);
893  }
894 
895  break;
896 
897  /* For lt, we start right before the key */
898  case afw_query_criteria_filter_op_id_lt:
899  rc = mdb_cursor_get(self->cursor, &self->key,
900  &self->data, MDB_SET_RANGE);
901 
902  /* MDB_SET_RANGE will always set at or just past
903  our key. So, we go one item backwards */
904  rc = mdb_cursor_get(self->cursor, &self->key,
905  &self->data, MDB_PREV);
906 
907  break;
908 
909  /* For gt, we start right after this key */
910  case afw_query_criteria_filter_op_id_gt:
911  rc = mdb_cursor_get(self->cursor, &self->key,
912  &self->data, MDB_SET_RANGE);
913  if (self->key_string) {
914  if (memcmp(self->key.mv_data, self->key_string->s,
915  self->key_string->len) == 0) {
916  /* if we've landed at our key, go past it */
917  rc = mdb_cursor_get(self->cursor, &self->key,
918  &self->data, MDB_NEXT_NODUP);
919  }
920  }
921 
922  break;
923 
924  default:
925  AFW_THROW_ERROR_Z(general,
926  "Unable to create cursor for this operator", xctx);
927  break;
928  }
929 }
930 
931 afw_adaptor_impl_index_cursor_t * afw_lmdb_internal_cursor_create(
932  const afw_lmdb_adaptor_session_t *session,
933  const afw_utf8_t * database,
934  const afw_utf8_t * object_type_id,
935  const afw_utf8_t * value,
936  const afw_query_criteria_filter_op_id_t operator,
937  afw_boolean_t unique,
938  afw_xctx_t *xctx)
939 {
941  MDB_dbi dbi;
942  MDB_txn *txn = session->currTxn;
943 
945  self->pub.inf = &impl_afw_adaptor_impl_index_cursor_inf;
946 
947  self->session = session;
948  self->object_type_id = object_type_id;
949  self->unique = unique;
950  self->operator = operator;
951 
952  dbi = afw_lmdb_internal_open_database(self->session->adaptor,
953  txn, database, 0, xctx->p, xctx);
954 
955  self->cursor = afw_lmdb_internal_open_cursor(session, dbi, xctx);
956  if (self->cursor == NULL) {
957  AFW_THROW_ERROR_Z(general,
958  "Error opening index cursor.", xctx);
959  }
960 
961  if (value) {
962  self->key_string = afw_utf8_create_copy(
963  value->s, value->len, xctx->p, xctx);
964  } else
965  self->key_string = NULL;
966 
967  afw_lmdb_internal_cursor_reset(self, xctx);
968 
969  /* also open the primary database to resolve references */
970  self->dbPri = afw_lmdb_internal_open_database(self->session->adaptor,
971  txn, &afw_lmdb_s_Primary, 0, xctx->p, xctx);
972 
973  /* Return new instance. */
974  return (afw_adaptor_impl_index_cursor_t *)self;
975 }
976 
977 /*
978  * Implementation of method release of interface afw_adaptor_impl_index_cursor.
979  */
980 void
981 impl_afw_adaptor_impl_index_cursor_release (
982  const afw_adaptor_impl_index_cursor_t * instance,
983  afw_xctx_t *xctx)
984 {
985  /* Assign instance pointer to self. */
988 
989  mdb_cursor_close(self->cursor);
990 }
991 
992 /*
993  * This routine uses the operator to decide how to move the
994  * cursor to the next object.
995  */
996 int afw_lmdb_internal_cursor_next(
997  const afw_adaptor_impl_index_cursor_t * instance,
998  afw_xctx_t *xctx)
999 {
1002  int rc = -1;
1003 
1005  switch (self->operator) {
1006  case afw_query_criteria_filter_op_id_ne:
1007  rc = mdb_cursor_get(self->cursor, &self->key,
1008  &self->data, MDB_NEXT);
1009  if (self->key_string) {
1010  if (memcmp(self->key.mv_data, self->key_string->s,
1011  self->key_string->len) == 0) {
1012  /* if we've landed at our key, go past it */
1013  rc = mdb_cursor_get(self->cursor, &self->key,
1014  &self->data, MDB_NEXT_NODUP);
1015  }
1016  }
1017  break;
1018 
1019  case afw_query_criteria_filter_op_id_eq:
1020  /* If this is a unique constraint index, MDB_NEXT_DUP will
1021  not do the right thing */
1022  if (self->unique)
1023  return -1;
1024 
1025  rc = mdb_cursor_get(self->cursor, &self->key,
1026  &self->data, MDB_NEXT_DUP);
1027  break;
1028 
1029  case afw_query_criteria_filter_op_id_gt:
1032  case afw_query_criteria_filter_op_id_ge:
1033 
1034  rc = mdb_cursor_get(self->cursor, &self->key,
1035  &self->data, MDB_NEXT);
1036  break;
1037 
1038  case afw_query_criteria_filter_op_id_lt:
1041  case afw_query_criteria_filter_op_id_le:
1042 
1043  rc = mdb_cursor_get(self->cursor, &self->key,
1044  &self->data, MDB_PREV);
1045  break;
1046 
1047  default:
1048  break;
1049  }
1050 
1051  return rc;
1052 }
1053 
1054 /*
1055  * Implementation of method get_next_object of interface afw_adaptor_impl_index_cursor.
1056  */
1057 const afw_object_t *
1058 impl_afw_adaptor_impl_index_cursor_get_next_object (
1059  const afw_adaptor_impl_index_cursor_t * instance,
1060  const afw_pool_t *pool,
1061  afw_xctx_t *xctx)
1062 {
1063  /* Assign instance pointer to self. */
1066  const afw_lmdb_adaptor_t *adaptor = self->session->adaptor;
1067  const afw_object_t *object = NULL;
1068  const afw_value_t *value;
1069  const afw_utf8_t *object_id;
1070  afw_utf8_t object_type;
1071  afw_memory_t from_raw;
1072  afw_memory_t rawKey;
1073  afw_uuid_t uuid;
1074  MDB_val key, data;
1075  MDB_txn *txn;
1076  int rc;
1077 
1078  do {
1079  /* the cursor may be empty or finished */
1080  if (self->data.mv_data == NULL)
1081  return NULL;
1082 
1083  memset(&data, 0, sizeof(MDB_val));
1084  key.mv_data = (void *)self->data.mv_data;
1085  key.mv_size = self->data.mv_size;
1086  rawKey.ptr = key.mv_data;
1087  rawKey.size = key.mv_size;
1088 
1089  txn = self->session->currTxn;
1090 
1091  /* now, fetch the actual object from the primary database */
1092  rc = mdb_get(txn, self->dbPri, &key, &data);
1093  if (rc == MDB_NOTFOUND) {
1094  /* indicates an index was not cleaned up */
1095  rc = afw_lmdb_internal_cursor_next(instance, xctx);
1096  if (rc) {
1097  return NULL;
1098  }
1099  continue;
1100  } else if (rc) {
1101  AFW_THROW_ERROR_RV_Z(general, lmdb, rc,
1102  "Error in mdb_get()!", xctx);
1103  }
1104 
1105  afw_lmdb_internal_get_key(&rawKey, &object_type, &uuid);
1106  object_id = afw_uuid_to_utf8(&uuid, pool, xctx);
1107 
1108  from_raw.ptr = data.mv_data;
1109  from_raw.size = data.mv_size;
1110 
1112  adaptor->ubjson, &from_raw, NULL, pool, xctx);
1113 
1114  object = afw_value_as_object(value, xctx);
1115 
1116  afw_object_meta_set_ids(object, &adaptor->pub.adaptor_id,
1117  self->object_type_id, object_id, xctx);
1118 
1119  /* advance the cursor for the next object */
1120  rc = afw_lmdb_internal_cursor_next(instance, xctx);
1121  if (rc) {
1122  /* end of data */
1123  self->data.mv_data = NULL;
1124  }
1125 
1126  } while (object == NULL && rc == MDB_NOTFOUND);
1127 
1128  return object;
1129 }
1130 
1131 
1132 /*
1133  * Implementation of method contains_object of interface afw_adaptor_impl_index_cursor.
1134  *
1135  */
1138  const afw_adaptor_impl_index_cursor_t * instance,
1139  const afw_object_t * object,
1140  afw_xctx_t *xctx)
1141 {
1142  /* Assign instance pointer to self. */
1145  const afw_utf8_t *object_id, *key_string;
1146  afw_memory_t index;
1147  MDB_val key, data;
1148  afw_boolean_t contains = false;
1149  const afw_uuid_t *uuid;
1150  int rc;
1151 
1156  if (self->operator != afw_query_criteria_filter_op_id_eq) {
1157  AFW_THROW_ERROR_Z(general,
1158  "The contains_object method for LMDB cursors only implements eq", xctx);
1159  }
1160 
1161  object_id = afw_object_meta_get_object_id(object, xctx);
1162  uuid = afw_uuid_from_utf8(object_id, xctx->p, xctx);
1163 
1164  afw_lmdb_internal_set_key(&index,
1165  self->object_type_id, uuid, xctx->p, xctx);
1166 
1167  key_string = afw_utf8_create_copy(
1168  self->key_string->s, self->key_string->len, xctx->p, xctx);
1169 
1170  key.mv_data = (void *)key_string->s;
1171  key.mv_size = key_string->len;
1172  data.mv_data = (void *)index.ptr;
1173  data.mv_size = index.size;
1174 
1175  rc = mdb_cursor_get(self->cursor, &key, &data, MDB_GET_BOTH);
1176  if (rc == 0) {
1177  /*
1178  I don't feel like I should have to do this, but MDB_GET_BOTH
1179  likes to return random records from time to time, which do not
1180  match the key/data I've supplied. The documentation on this
1181  operand is slim to say the least. Some google searches indicate
1182  bugginess with this call/operand, even recently.
1183  */
1184  if (memcmp(data.mv_data, index.ptr, index.size) == 0) {
1185  contains = true;
1186  }
1187  }
1188 
1189  /* reset the cursor to point to our previous entry */
1190  afw_lmdb_internal_cursor_reset(self, xctx);
1191 
1192  return contains;
1193 }
1194 
1195 
1196 
1197 /*
1198  * Implementation of method inner_join of interface afw_adaptor_impl_index_cursor.
1199  *
1200  */
1203  const afw_adaptor_impl_index_cursor_t * instance,
1204  const afw_adaptor_impl_index_cursor_t * cursor,
1205  afw_xctx_t *xctx)
1206 {
1207  /* Assign instance pointer to self. */
1212  size_t count_this, count_that;AFW_POSSIBLY_UNUSED_VARIABLE int rc;
1214 
1215  rc = mdb_cursor_count(self->cursor, &count_this);
1216  rc = mdb_cursor_count(that->cursor, &count_that);
1217 
1218  /* use the cursor count and return the smaller set */
1219  if (count_this < count_that)
1220  return instance;
1221  else
1222  return cursor;
1223 }
1224 
1225 /*
1226  * Implementation of method get_count of interface afw_adaptor_impl_index_cursor.
1227  */
1229 impl_afw_adaptor_impl_index_cursor_get_count(
1230  const afw_adaptor_impl_index_cursor_t * instance,
1231  size_t * count,
1232  afw_xctx_t *xctx)
1233 {
1236  int rc;
1237 
1238  /* we can only calculate the count for duplicate data on a single key,
1239  so this means only eq operators are supported. */
1240  if (self->operator != afw_query_criteria_filter_op_id_eq) {
1241  return false;
1242  }
1243 
1244  rc = mdb_cursor_count(self->cursor, count);
1245 
1246  return (rc == 0) ? true : false;
1247 }
1248 
1249 
1250 
1251 /*
1252  * Implementation of Transaction interface.
1253  *
1254  * The following routines implement the functions necessary to
1255  * implement a transaction.
1256  */
1258  afw_lmdb_adaptor_session_t *session,
1259  afw_xctx_t *xctx)
1260 {
1261  afw_lmdb_transaction_t *self;
1262  int rc;
1263 
1265  self->pub.inf = &impl_afw_adaptor_transaction_inf;
1266 
1267  self->session = (afw_adaptor_session_t *)session;
1268 
1269  /*
1270  Anytime we need to begin a transaction, we must first obtain
1271  a database lock to prevent another transactions from opening
1272  databases.
1273  */
1274  apr_thread_rwlock_rdlock(session->adaptor->dbLock);
1275 
1276  rc = mdb_txn_begin(session->adaptor->dbEnv, NULL, 0, &self->txn);
1277  if (rc) {
1278  apr_thread_rwlock_unlock(session->adaptor->dbLock);
1279 
1280  AFW_THROW_ERROR_RV_Z(general, lmdb, rc,
1281  "Unable to begin transaction.", xctx);
1282  }
1283 
1284  session->transaction = self;
1285  session->currTxn = self->txn;
1286 
1287  return self;
1288 }
1289 
1290 
1291 
1292 
1293 /*
1294  * Implementation of method release of interface afw_adaptor_transaction.
1295  */
1296 void
1297 impl_afw_adaptor_transaction_release (
1298  const afw_adaptor_transaction_t * instance,
1299  afw_xctx_t *xctx)
1300 {
1301  /* Assign instance pointer to self. */
1302  afw_lmdb_transaction_t * self =
1303  (afw_lmdb_transaction_t *)instance;
1304  afw_lmdb_adaptor_session_t * session =
1305  (afw_lmdb_adaptor_session_t *)self->session;
1306 
1307  /* if our session still has an active transaction going, abort it */
1308  if (session->transaction) {
1309  mdb_txn_abort(self->txn);
1310  apr_thread_rwlock_unlock(session->adaptor->dbLock);
1311  }
1312 
1313  self->txn = NULL;
1314  session->transaction = NULL;
1315 }
1316 
1317 
1318 
1319 /*
1320  * Implementation of method commit of interface afw_adaptor_transaction.
1321  */
1322 void
1323 impl_afw_adaptor_transaction_commit (
1324  const afw_adaptor_transaction_t * instance,
1325  afw_xctx_t *xctx)
1326 {
1327  /* Assign instance pointer to self. */
1328  afw_lmdb_transaction_t * self =
1329  (afw_lmdb_transaction_t *)instance;
1330  afw_lmdb_adaptor_session_t * session =
1331  (afw_lmdb_adaptor_session_t *)self->session;
1332  int rc;
1333 
1334  rc = mdb_txn_commit(self->txn);
1335  if (rc) {
1336  apr_thread_rwlock_unlock(session->adaptor->dbLock);
1337 
1338  AFW_THROW_ERROR_RV_Z(general, lmdb, rc,
1339  "Unable to commit transaction.", xctx);
1340  }
1341 
1342  apr_thread_rwlock_unlock(session->adaptor->dbLock);
1343 
1344  /* clear our session transaction to prevent further commits */
1345  self->txn = NULL;
1346  session->transaction = NULL;
1347 }
1348 
1349 /*
1350  * Used by adaptive function to perform a reader_check to clear any
1351  * stale readers of the database.
1352  */
1353 int
1354 afw_lmdb_internal_reader_check(
1355  const afw_adaptor_t * instance,
1356  int * deadReaders,
1357  afw_xctx_t *xctx)
1358 {
1359  int rc;
1360  afw_lmdb_adaptor_t * self =
1361  (afw_lmdb_adaptor_t *)instance;
1362 
1363  rc = mdb_reader_check(self->dbEnv, deadReaders);
1364 
1365  return rc;
1366 }
1367 
1368 typedef struct {
1369  const afw_lmdb_adaptor_t * adaptor;
1370  const afw_pool_t * pool;
1371  afw_xctx_t * xctx;
1372  const afw_utf8_t ** list;
1374 
1375 int afw_lmdb_internal_reader_list_cb(
1376  const char *msg,
1377  void *ctx)
1378 {
1380  const afw_utf8_t *message;
1381 
1382  message = afw_utf8_from_utf8_z(msg, context->pool, context->xctx);
1383  *(context->list) = afw_utf8_create_copy(
1384  message->s, message->len, context->pool, context->xctx);
1385 
1386  return (int)strlen(msg);
1387 }
1388 
1389 /*
1390  * Used by adaptive function to perform a reader_list to print a list of
1391  * current readers of the database, including their pid and thread id's.
1392  */
1393 int afw_lmdb_internal_reader_list(
1394  const afw_adaptor_t * instance,
1395  const afw_utf8_t ** list,
1396  const afw_pool_t * pool,
1397  afw_xctx_t *xctx)
1398 {
1399  int rc;
1400  afw_lmdb_adaptor_t * self =
1401  (afw_lmdb_adaptor_t *)instance;
1403 
1404  ctx.adaptor = self;
1405  ctx.pool = pool;
1406  ctx.xctx = xctx;
1407  ctx.list = list;
1408 
1409  rc = mdb_reader_list(self->dbEnv, afw_lmdb_internal_reader_list_cb, &ctx);
1410 
1411  return rc;
1412 }
Adaptive Framework Core API.
Interface afw_interface implementation declares.
Interface afw_interface implementation declares.
Interface afw_interface implementation declares.
Adaptive Framework register generated (afw_lmdb) header.
Adaptive Framework LMDB Adaptor Internal Header.
afw_lmdb_transaction_t * afw_lmdb_transaction_create(afw_lmdb_adaptor_session_t *session, afw_xctx_t *xctx)
Internal create a LMDB adaptor transaction.
#define AFW_LMDB_END_TRANSACTION()
End an LMDB transaction.
#define AFW_LMDB_BEGIN_TRANSACTION(adaptor, session, flags, exclusive, xctx)
Begin an LMDB transaction.
afw_lmdb_key_value_t * afw_lmdb_key_value_create(afw_lmdb_adaptor_session_t *session, afw_xctx_t *xctx)
Internal create a LMDB adaptor key/value.
#define AFW_LMDB_COMMIT_TRANSACTION()
Commit a transaction.
Adaptive Framework UUID header.
const afw_adaptor_impl_index_cursor_t * impl_afw_adaptor_impl_index_cursor_inner_join(const afw_adaptor_impl_index_cursor_t *instance, const afw_adaptor_impl_index_cursor_t *cursor, afw_xctx_t *xctx)
afw_boolean_t impl_afw_adaptor_impl_index_cursor_contains_object(const afw_adaptor_impl_index_cursor_t *instance, const afw_object_t *object, afw_xctx_t *xctx)
const afw_memory_t * impl_afw_adaptor_key_value_get(const afw_adaptor_key_value_t *instance, const afw_utf8_t *namespace, const afw_memory_t *key, afw_xctx_t *xctx)
void impl_afw_adaptor_key_value_add(const afw_adaptor_key_value_t *instance, const afw_utf8_t *namespace, const afw_memory_t *key, const afw_memory_t *value, afw_xctx_t *xctx)
afw_value_as_object(const afw_value_t *value, afw_xctx_t *xctx)
Typesafe cast of data type object.
#define AFW_UTF8_FMT_ARG(A_STRING)
Convenience Macro for use with AFW_UTF8_FMT to specify arg.
Definition: afw_common.h:605
#define AFW_UTF8_LITERAL(A_STRING)
String literal initializer.
Definition: afw_common.h:582
_Bool afw_boolean_t
Definition: afw_common.h:373
#define AFW_UTF8_FMT
Format string specifier used for afw_utf8_t.
Definition: afw_common.h:588
char afw_utf8_octet_t
8 bits of utf-8 codepoint.
Definition: afw_common.h:236
#define AFW_POSSIBLY_UNUSED_VARIABLE
Macro to avoid unused variable warning.
Definition: afw_common.h:127
int afw_rc_t
Definition: afw_common.h:663
#define afw_content_type_raw_to_value(instance, raw, source_location, p, xctx)
Call method raw_to_value of interface afw_content_type.
#define afw_content_type_object_to_raw(instance, object, options, p, xctx)
Convert object to the raw in specified pool.
#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
#define AFW_THROW_ERROR_RV_FZ(code, rv_source_id, rv, xctx, format_z,...)
Macro used to set error and rv in xctx and throw it.
Definition: afw_error.h:338
#define AFW_THROW_ERROR_Z(code, message_z, xctx)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:283
afw_object_meta_set_ids(const afw_object_t *instance, const afw_utf8_t *adaptor_id, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, afw_xctx_t *xctx)
Set object's ids.
afw_object_meta_get_object_id(const afw_object_t *instance, afw_xctx_t *xctx)
Get entity object's object id.
afw_object_options_essential
Object processing options - metaLimited.
#define afw_object_create_managed(p, xctx)
Create an empty entity object in its own pool.
Definition: afw_object.h:913
afw_object_set_property(const afw_object_t *instance, const afw_utf8_t *property_name, const afw_value_t *value, afw_xctx_t *xctx)
Set the value of an object's property.
Definition: afw_object.c:46
#define afw_pool_malloc(instance, size, xctx)
Call method malloc of interface afw_pool.
#define afw_pool_calloc_type(instance, type, xctx)
Macro to allocate cleared memory to hold type in pool.
Definition: afw_pool.h:167
afw_query_criteria_filter_op_id_t
const afw_utf8_t * afw_utf8_concat(const afw_pool_t *p, afw_xctx_t *xctx,...)
Concatenate strings with result in specifed pool.
#define afw_utf8_create_copy(s, len, p, xctx)
Make a utf-8 sting from chars in pool specified.
Definition: afw_utf8.h:369
#define afw_utf8_from_utf8_z(s_z, p, xctx)
Make utf-8 string without copy in specified pool.
Definition: afw_utf8.h:254
const afw_utf8_z_t * afw_utf8_to_utf8_z(const afw_utf8_t *string, const afw_pool_t *p, afw_xctx_t *xctx)
Convert utf8 to utf8_z in specified pool.
Definition: afw_utf8.h:529
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
afw_uuid_to_utf8(const afw_uuid_t *uuid, const afw_pool_t *p, afw_xctx_t *xctx)
Convert uuid to a standard format UUID utf-8 string.
Definition: afw_uuid.c:79
afw_value_create_dateTime_now_utc(const afw_pool_t *p, afw_xctx_t *xctx)
Create a dateTime value with current time.
Definition: afw_value.c:736
#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_key_value public struct.
Interface afw_adaptor public struct.
Interface afw_adaptor_session public struct.
Interface afw_adaptor_transaction 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_value public struct.
Interface afw_xctx public struct.