3 Commits 7b33caeca4 ... 1c30902eae

Author SHA1 Message Date
  scossu 1c30902eae Log MDB transaction begin & commit. 1 month ago
  scossu 40a8636ba6 Doc enhancements. 1 month ago
  scossu 3c8a126041 Fix Makefile mess-up. It was Vim's fault. 1 month ago
8 changed files with 190 additions and 147 deletions
  1. 1 1
      Makefile
  2. 5 3
      README.md
  3. 111 104
      include/volksdata/codec.h
  4. 18 11
      include/volksdata/core.h
  5. 6 0
      include/volksdata/store.h
  6. 3 1
      src/codec/codec_ttl.c
  7. 46 23
      src/store_mdb.c
  8. 0 4
      src/term.c

+ 1 - 1
Makefile

@@ -121,7 +121,7 @@ export LD_LIBRARY_PATH = $(OUTDIR):$(libdir)
 help:
 	@echo "Command overview:"; echo; \
 		grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
-		| SED -N 'S/^\(.*\): \(.*\)##\(.*\)/\1|\3/P' \
+		| sed -n 's/^\(.*\): \(.*\)##\(.*\)/\1|\3/p' \
 		| column -t  -s '|'
 	
 

+ 5 - 3
README.md

@@ -27,9 +27,11 @@ minimal changes to the core. More documentation on the topic will follow.
 
 ## Development Status
 
-**Beta.** Considered feature-complete from an MVP standpoint. The API may
-change significantly. Most commits (not prefixed with "WIP") and merges to the
-master branch are pushed after tests pass with a clean `make memcheck` output.
+**Beta.** Considered feature-complete from an MVP standpoint. The API may still
+change significantly. Any API changes are marked by a new `v1.0b<n>` tag.  Most
+commits (not prefixed with "WIP") and merges to the master branch are pushed
+after tests pass with a clean `make memcheck` output (and on a good day, with a
+clean `doxygen`).
 
 Test coverage is not sufficient. Documentation is fairly extensive but needs
 reformatting. This code is being integrated in higher-level projects and is

+ 111 - 104
include/volksdata/codec.h

@@ -3,7 +3,7 @@
 
 #include "volksdata/graph.h"
 
-/** @defgroup codec RDF codec module
+/** @defgroup codec_priv Private RDF codec API
  * @ingroup private
  * @{
  */
@@ -41,6 +41,115 @@ typedef struct parse_error_t {
 */
 
 
+/*
+ * Common utility functions.
+ */
+
+/** @brief strdup() for unsigned char.
+ *
+ * This is to be used with uint8_t sequences considered to be UTF-8 sequences,
+ * requird by re2c (it won't work with byte sequences containing `NUL`).
+ */
+inline uint8_t
+*uint8_dup (const uint8_t *str)
+{ return (uint8_t *) strdup ((char *) str); }
+
+
+/** @brief strndup() for unsigned char.
+ *
+ * This is to be used with uint8_t sequences considered to be UTF-8 sequences,
+ * requird by re2c (it won't work with byte sequences containing `NUL`).
+ */
+inline uint8_t
+*uint8_ndup (const uint8_t *str, size_t size)
+{ return (uint8_t *) strndup ((char *) str, size); }
+
+
+/** @brief Add escape character (backslash) to illegal literal characters.
+ *
+ * @param[in] in Input string.
+ *
+ * @param[out] out Output string.
+ *
+ * @return VOLK_OK on success; VOLK_MEM_ERR on memory error.
+ */
+VOLK_rc
+escape_lit (const char *in, char **out);
+
+
+/** @brief Replace non-printable characters with their literal byte.
+ *
+ *  Escape backslash is to be added separately.
+ */
+static inline char
+escape_char (const char c) {
+    switch (c) {
+        case '\t': return 't';
+        case '\b': return 'b';
+        case '\n': return 'n';
+        case '\r': return 'r';
+        case '\f': return 'f';
+        default: return c;
+    }
+}
+
+
+/** @brief Unescape a single character.
+ *
+ * Convert escaped special characters such as `\t`, `\n`, etc. into their
+ * corresponding code points.
+ *
+ * Non-special characters are returned unchanged.
+ *
+ * @param[in] c Character to unescape. Note that this is the single character
+ * after `\`.
+ * 
+ * @return Code point corresponding to the escaped character.
+ */
+inline char
+unescape_char (const char c)
+{
+    switch (c) {
+        case 't': return '\t';
+        case 'b': return '\b';
+        case 'n': return '\n';
+        case 'r': return '\r';
+        case 'f': return '\f';
+        default: return c;
+    }
+}
+
+
+/** @brief Replace `\uxxxx` and `\Uxxxxxxxx` with Unicode bytes.
+ *
+ * @param[in] esc_str Escaped string.
+ *
+ * @param[in] size Maximum number of characters to scan, à la strncpy().
+ *
+ * @return String with escape sequences replaced by Unicode bytes.
+ */
+uint8_t *unescape_unicode (const uint8_t *esc_str, size_t size);
+
+
+/** @brief Format an informational header.
+ *
+ * The information includes software version and current date. It is terminated
+ * by a newline + NUL and prefixed with the string specified in `pfx`. It is
+ * NOT prefixed by any comment characters.
+ *
+ * @param[in] pfx Prefix to add to the string. It may be a comment starter,
+ *  such as `# `.
+ */
+char *fmt_header (char *pfx);
+
+/// @}  END defgroup codec_priv
+
+
+/** @defgroup codec_p RDF encoder & decoder module
+ * @ingroup public
+ * @{
+ */
+
 /*
  * Interface prototypes.
  */
@@ -189,108 +298,6 @@ struct codec_t {
     term_decode_fn_t    decode_term;        ///< Term decoder function.
     gr_decode_fn_t      decode_graph;       ///< Graph decoder function.
 };
-
-
-/*
- * Common utility functions.
- */
-
-/** @brief strdup() for unsigned char.
- *
- * This is to be used with uint8_t sequences considered to be UTF-8 sequences,
- * requird by re2c (it won't work with byte sequences containing `NUL`).
- */
-inline uint8_t
-*uint8_dup (const uint8_t *str)
-{ return (uint8_t *) strdup ((char *) str); }
-
-
-/** @brief strndup() for unsigned char.
- *
- * This is to be used with uint8_t sequences considered to be UTF-8 sequences,
- * requird by re2c (it won't work with byte sequences containing `NUL`).
- */
-inline uint8_t
-*uint8_ndup (const uint8_t *str, size_t size)
-{ return (uint8_t *) strndup ((char *) str, size); }
-
-
-/** @brief Add escape character (backslash) to illegal literal characters.
- *
- * @param[in] in Input string.
- *
- * @param[out] out Output string.
- *
- * @return VOLK_OK on success; VOLK_MEM_ERR on memory error.
- */
-VOLK_rc
-escape_lit (const char *in, char **out);
-
-
-/** @brief Replace non-printable characters with their literal byte.
- *
- *  Escape backslash is to be added separately.
- */
-static inline char
-escape_char (const char c) {
-    switch (c) {
-        case '\t': return 't';
-        case '\b': return 'b';
-        case '\n': return 'n';
-        case '\r': return 'r';
-        case '\f': return 'f';
-        default: return c;
-    }
-}
-
-
-/** @brief Unescape a single character.
- *
- * Convert escaped special characters such as `\t`, `\n`, etc. into their
- * corresponding code points.
- *
- * Non-special characters are returned unchanged.
- *
- * @param[in] c Character to unescape. Note that this is the single character
- * after `\`.
- * 
- * @return Code point corresponding to the escaped character.
- */
-inline char
-unescape_char (const char c)
-{
-    switch (c) {
-        case 't': return '\t';
-        case 'b': return '\b';
-        case 'n': return '\n';
-        case 'r': return '\r';
-        case 'f': return '\f';
-        default: return c;
-    }
-}
-
-
-/** @brief Replace `\uxxxx` and `\Uxxxxxxxx` with Unicode bytes.
- *
- * @param[in] esc_str Escaped string.
- *
- * @param[in] size Maximum number of characters to scan, à la strncpy().
- *
- * @return String with escape sequences replaced by Unicode bytes.
- */
-uint8_t *unescape_unicode (const uint8_t *esc_str, size_t size);
-
-
-/** @brief Format an informational header.
- *
- * The information includes software version and current date. It is terminated
- * by a newline + NUL and prefixed with the string specified in `pfx`. It is
- * NOT prefixed by any comment characters.
- *
- * @param[in] pfx Prefix to add to the string. It may be a comment starter,
- *  such as `# `.
- */
-char *fmt_header (char *pfx);
-
 /// @}  END defgroup codec
+
 #endif

+ 18 - 11
include/volksdata/core.h

@@ -44,7 +44,13 @@
 // TODO Cross-platform ramdisk path.
 #define TMPDIR "/tmp"
 
-/** @defgroup public LSUP Public API
+/** @defgroup public Public Volksdata API
+ * @{
+ */
+
+/**@defgroup const Constants
+ *
+ * @ingroup public
  * @{
  */
 #define VOLK_NS "urn:lsup:"             /// Default LS namespace.
@@ -59,6 +65,7 @@
 /** @brief "NULL" triple, a value that is never user-provided.
  */
 #define NULL_TRP {NULL_KEY, NULL_KEY, NULL_KEY}
+/// @} END defgroup const
 
 
 /* * * RETURN CODES * * */
@@ -235,7 +242,7 @@ VOLK_strerror (VOLK_rc rc);
 /// @} END defgroup public
 
 
-/** @defgroup private Private LSUP API
+/** @defgroup private Private Volksdata API
  * @{
  */
 /// minimum error value.
@@ -269,7 +276,7 @@ VOLK_strerror (VOLK_rc rc);
 #define LOG_RC(rc) do {                                             \
     if ((rc) < 0) log_error (VOLK_strerror (rc));                   \
     else if ((rc) > 0) log_warn (VOLK_strerror (rc));              \
-} while (0);
+} while (0)
 
 /// Jump to `marker` if `exp` does not return `VOLK_OK`.
 #define CHECK(exp, marker) do {                                     \
@@ -281,7 +288,7 @@ VOLK_strerror (VOLK_rc rc);
                 VOLK_strerror (_rc));                               \
         goto marker;                                                \
     }                                                               \
-} while (0);
+} while (0)
 
 /// Jump to `marker` if `exp` returns a negative value (skip warnings).
 #define PCHECK(exp, marker) do {                                    \
@@ -293,7 +300,7 @@ VOLK_strerror (VOLK_rc rc);
         LOG_RC(_rc);                                                \
         goto marker;                                                \
     }                                                               \
-} while (0);
+} while (0)
 
 /// Log error and jump to `marker` if `exp` is NULL.
 #define NLCHECK(exp, marker) do {                                   \
@@ -312,7 +319,7 @@ VOLK_strerror (VOLK_rc rc);
                 VOLK_strerror (_rc));                               \
         return _rc;                                                 \
     }                                                               \
-} while (0);
+} while (0)
 
 /// Return `exp` return value if it is of `VOLK_rc` type and negative (=error)
 #define PRCCK(exp) do {                                             \
@@ -323,7 +330,7 @@ VOLK_strerror (VOLK_rc rc);
                 VOLK_strerror (_rc));                               \
         return _rc;                                                 \
     }                                                               \
-} while (0);
+} while (0)
 
 /// Return `NULL` if `exp` returns a nonzero value.
 #define RCNL(exp) do {                                              \
@@ -345,7 +352,7 @@ VOLK_strerror (VOLK_rc rc);
                 VOLK_strerror (_rc));                               \
         return NULL;                                                \
     }                                                               \
-} while (0);
+} while (0)
 
 /// Log error and return NULL if `exp` is NULL.
 #define NLNL(exp) do {                                              \
@@ -353,19 +360,19 @@ VOLK_strerror (VOLK_rc rc);
         log_error ("*** PREMATURE EXIT due to NULL result.");       \
         return NULL;                                                \
     }                                                               \
-} while (0);
+} while (0)
 
 /// Allocate one pointer with malloc and return rc if it fails.
 #define MALLOC_GUARD(var, rc) do {                                  \
     (var) = malloc (sizeof *(var));                                 \
     if (UNLIKELY (var == NULL)) return (rc);                        \
-} while (0);
+} while (0)
 
 /// Allocate one pointer with calloc and return rc if it fails.
 #define CALLOC_GUARD(var, rc) do {                                  \
     (var) = calloc (1, sizeof *(var));                              \
     if (UNLIKELY (var == NULL)) return (rc);                        \
-} while (0);
+} while (0)
 
 /*
 #define MALLOC_GUARD_ME(var) MALLOC_GUARD((var), VOLK_MEM_ERR)      \

+ 6 - 0
include/volksdata/store.h

@@ -122,6 +122,12 @@ void
 VOLK_store_free (VOLK_Store *store);
 
 
+/** @brief Store size in triples.
+ *
+ * @param[in] store Store handle.
+ *
+ * @return Number of triples in the store, across all contexts.
+ */
 size_t
 VOLK_store_size (const VOLK_Store *store);
 

+ 3 - 1
src/codec/codec_ttl.c

@@ -6,7 +6,9 @@
  * This iterator yields one or more triples at a time, one group per subject,
  * with the most compact form allowed by Turtle, e.g.
  *
- * ```:s :p1 :o1, :o2, o3; :p2 :o4, :o5, <http://example.com/ext1> .```
+ * ```
+ * :s :p1 :o1, :o2, o3; :p2 :o4, :o5, <http://example.com/ext1> .
+ * ```
  */
 typedef struct {
     const VOLK_Codec *  codec;      ///< Codec that generated this iterator.

+ 46 - 23
src/store_mdb.c

@@ -20,7 +20,6 @@
 #define ENV_DIR_MODE 0750
 #define ENV_FILE_MODE 0640
 
-
 /*
  * Data types.
  */
@@ -229,6 +228,29 @@ mdbstore_path_from_id (const char *id)
 }
 
 
+/*
+ * Inliners.
+ */
+
+static inline VOLK_rc
+txn_begin (MDB_env *env, MDB_txn *p, unsigned int f, MDB_txn **tp) {
+    VOLK_rc rc = mdb_txn_begin (env, p, f, tp);
+    const char *path;
+    mdb_env_get_path (env, &path);
+    LOG_DEBUG (
+            "BEGIN %s transaction %p child of %p in env %s",
+            f == 0 ? "RW" : "RO", *tp, p, path);
+    return rc;
+}
+
+
+static inline VOLK_rc
+txn_commit (MDB_txn *t) {
+    LOG_DEBUG ("COMMIT transaction %p", t);
+    return mdb_txn_commit (t);
+}
+
+
 /**
  * Store interface.
  */
@@ -260,7 +282,7 @@ mdbstore_setup (const char *id, bool clear)
     LOG_DEBUG("Environment opened at %s.", path);
 
     MDB_txn *txn;
-    RCCK (mdb_txn_begin (env, NULL, 0, &txn));
+    RCCK (txn_begin (env, NULL, 0, &txn));
     MDB_dbi dbi;
     for (int i = 0; i < N_DB; i++) {
         LOG_TRACE("Creating DB %s", db_labels[i]);
@@ -292,7 +314,7 @@ mdbstore_setup (const char *id, bool clear)
         CHECK (db_rc, fail);
     }
 
-    mdb_txn_commit (txn);
+    CHECK (txn_commit (txn), fail);
     mdb_env_close (env);
 
     return clear ? VOLK_OK : rc;
@@ -340,15 +362,17 @@ mdbstore_new (const char *id, size_t _unused)
     CHECK (mdb_env_open (store->env, path, 0, ENV_FILE_MODE), fail);
 
     // Assign DB handles to store->dbi.
-    CHECK (mdb_txn_begin (store->env, NULL, 0, &txn), fail);
+    CHECK (txn_begin (store->env, NULL, 0, &txn), fail);
     for (int i = 0; i < N_DB; i++)
         CHECK (mdb_dbi_open (
                 txn, db_labels[i], db_flags[i], store->dbi + i), fail);
 
     store->flags |= LSSTORE_OPEN;
-    mdb_txn_commit (txn);
+    CHECK (txn_commit(txn), fail);
     txn = NULL;
 
+    log_info ("Created environment at %s", path);
+
     return store;
 
 fail:
@@ -393,7 +417,7 @@ mdbstore_stat (const MDBStore *store, MDB_stat *stat)
     if (!(store->flags & LSSTORE_OPEN)) return 0;
 
     MDB_txn *txn;
-    mdb_txn_begin (store->env, NULL, MDB_RDONLY, &txn);
+    RCCK (txn_begin (store->env, NULL, MDB_RDONLY, &txn));
 
     if (mdb_stat (txn, store->dbi[IDX_SPO_C], stat) != MDB_SUCCESS)
         return VOLK_DB_ERR;
@@ -421,7 +445,7 @@ mdbstore_txn_begin (void *h, int flags, void **th)
 {
     MDBStore *store = h;
 
-    RCCK (mdb_txn_begin (store->env, NULL, flags, (MDB_txn **) th));
+    RCCK (txn_begin (store->env, NULL, flags, (MDB_txn **) th));
 
     return VOLK_OK;
 }
@@ -430,7 +454,7 @@ mdbstore_txn_begin (void *h, int flags, void **th)
 static VOLK_rc
 mdbstore_txn_commit (void *th)
 {
-    RCCK (mdb_txn_commit ((MDB_txn *) th));
+    RCCK (txn_commit ((MDB_txn *) th));
 
     return VOLK_OK;
 }
@@ -469,7 +493,7 @@ mdbstore_add_init (void *h, const VOLK_Buffer *sc, void *th)
     it->store = store;
     it->i = 0;
 
-    CHECK (mdb_txn_begin (store->env, (MDB_txn *) th, 0, &it->txn), fail);
+    CHECK (txn_begin (store->env, (MDB_txn *) th, 0, &it->txn), fail);
 
     if (sc) {
         // Store context if it's not the default one.
@@ -574,14 +598,16 @@ mdbstore_add_done (void *h)
 {
     MDBIterator *it = h;
     VOLK_rc rc = VOLK_OK;
+    log_debug ("Committing add transaction.");
 
-    if (mdb_txn_commit (it->txn) != MDB_SUCCESS) {
+    if (txn_commit (it->txn) != MDB_SUCCESS) {
         mdb_txn_abort (it->txn);
         rc = VOLK_TXN_ERR;
     }
 
     free (it);
 
+    RCCK (rc);
     return rc;
 }
 
@@ -648,7 +674,7 @@ mdbstore_lookup (
     if (th) it->txn = th;
     else if (!it->txn) {
         // Start RO transaction if not in a write txn already.
-        it->rc = mdb_txn_begin (it->store->env, NULL, MDB_RDONLY, &it->txn);
+        it->rc = txn_begin (it->store->env, NULL, MDB_RDONLY, &it->txn);
         if (it->rc != MDB_SUCCESS) {
             log_error ("Database error in lookup: %s", VOLK_strerror (it->rc));
             return NULL;
@@ -889,9 +915,7 @@ mdbstore_update_ctx (
     MDB_txn
         *p_txn = th,
         *txn;
-    CHECK (
-            rc = mdb_txn_begin (store->env, p_txn, 0, &txn),
-            finally);
+    CHECK (rc = txn_begin (store->env, p_txn, 0, &txn), finally);
 
     MDB_cursor *i_cur, *d_cur;
     CHECK (
@@ -1001,9 +1025,8 @@ close_d:
 close_i:
     mdb_cursor_close (i_cur);
 close_txn:
-    if (rc == VOLK_OK) {
-        RCCK (mdb_txn_commit (txn));
-    } else mdb_txn_abort (txn);
+    if (rc == VOLK_OK) RCCK (txn_commit (txn));
+    else mdb_txn_abort (txn);
 
     if (trp_data) free (trp_data);
 finally:
@@ -1028,7 +1051,7 @@ mdbstore_remove (
     ck = VOLK_buffer_hash (sc);
 
     MDB_txn *txn;
-    mdb_txn_begin (store->env, (MDB_txn *) th, 0, &txn);
+    RCCK (txn_begin (store->env, (MDB_txn *) th, 0, &txn));
 
     MDB_cursor *dcur, *icur;
     mdb_cursor_open (txn, store->dbi[IDX_SPO_C], &dcur);
@@ -1086,7 +1109,7 @@ mdbstore_remove (
 
     mdbiter_free (it);
 
-    if (UNLIKELY (mdb_txn_commit (txn) != MDB_SUCCESS)) {
+    if (UNLIKELY (txn_commit (txn) != MDB_SUCCESS)) {
         rc = VOLK_TXN_ERR;
         goto fail;
     }
@@ -1112,7 +1135,7 @@ mdbstore_tkey_exists (MDBStore *store, VOLK_Key tkey)
     key.mv_size = KLEN;
 
     MDB_txn *txn = NULL;
-    mdb_txn_begin (store->env, NULL, MDB_RDONLY, &txn);
+    txn_begin (store->env, NULL, MDB_RDONLY, &txn);
 
     MDB_cursor *cur = NULL;
     mdb_cursor_open (txn, store->dbi[IDX_T_ST], &cur);
@@ -1158,7 +1181,7 @@ mdbstore_add_term (void *h, const VOLK_Buffer *sterm, void *th)
     // close a new one.
     bool borrowed_txn = (th != NULL);
     if (borrowed_txn) txn = th;
-    else RCCK (mdb_txn_begin (store->env, NULL, 0, &txn));
+    else RCCK (txn_begin (store->env, NULL, 0, &txn));
 
     MDB_cursor *cur;
     CHECK (mdb_cursor_open (txn, store->dbi[IDX_T_ST], &cur), fail);
@@ -1173,7 +1196,7 @@ mdbstore_add_term (void *h, const VOLK_Buffer *sterm, void *th)
     db_rc = mdb_cursor_put (cur, &key, &data, MDB_NOOVERWRITE);
     if (db_rc != MDB_KEYEXIST) CHECK (db_rc, fail);
 
-    if (!borrowed_txn) CHECK (db_rc = mdb_txn_commit (txn), fail);
+    if (!borrowed_txn) CHECK (db_rc = txn_commit (txn), fail);
 
     return VOLK_OK;
 
@@ -1191,7 +1214,7 @@ mdbstore_ctx_list (void *h, void *th)
     VOLK_rc db_rc;
     MDB_txn *txn;
     if (th) txn = th;
-    else CHECK (mdb_txn_begin (store->env, NULL, MDB_RDONLY, &txn), fail);
+    else CHECK (txn_begin (store->env, NULL, MDB_RDONLY, &txn), fail);
 
 
     MDB_cursor *cur;

+ 0 - 4
src/term.c

@@ -1,10 +1,6 @@
 #include "volksdata/term.h"
 
 #define MAX_VALID_TERM_TYPE     VOLK_TERM_BNODE /* For type validation. */
-/// Bits to distinguish language-tagged literals.
-#define LIT_NONE 0
-#define LIT_NOLT 1
-#define LIT_LT 2
 
 
 /*