Bladeren bron

A few changes:

* Sync with LSUP_RDF updates.
* Add some utility functions.
* Create road map.
scossu 3 dagen geleden
bovenliggende
commit
3ba240d78c
8 gewijzigde bestanden met toevoegingen van 278 en 27 verwijderingen
  1. 1 1
      .gitignore
  2. 39 1
      README.md
  3. 35 0
      doc/ROADMAP.md
  4. 75 0
      doc/examples.md
  5. 28 2
      include/desc.h
  6. 57 19
      src/desc.c
  7. 6 2
      src/repo_core.c
  8. 37 2
      test/test_desc.c

+ 1 - 1
.gitignore

@@ -104,7 +104,7 @@ venv.bak/
 
 sandbox.c
 bin/*
-doc/
+doc/html/
 mdb_store/
 
 # IDE

+ 39 - 1
README.md

@@ -71,7 +71,45 @@ intended as goals.
 - Checksum of RDF resources (depends on `lsup_rdf` development)
 - Other features as they become necessary
 
-## Concepts
+## Conceptual framework
+
+### Storage areas
+
+While the underlying `LSUP_RDF` allows for working with multiple, user-defined
+storage back ends, `LSUP_REPO` has two designated stores: an in-memory staging
+area, where resources are created and manipulated, and a persistent storage
+backed by LMDB, where resources are pushed after they are modified. Functions
+such as `LSR_desc_new`, `LSR_desc_update`, and `LSR_desc_triples` operate on
+the staging area. Functions such as `LSR_desc_store`, `LRS_desc_get`, and
+`LSR_desc_delete` move resources between the staging area and the persistent
+store, or delete them from the persistent store.
+
+Usual actions in a resource lifecycle flow in `LSUP_REPO` may be, for example:
+
+- Creating a resource and adding some data graphs
+- Storing the staged resource
+- Retrieving a resource from storage into memory
+- Modifying the staged resource via add and remove triple sets
+- Storing the modified resource
+- Deleting a stored resource
+
+### Versioning and soft & hard deletion
+
+`LSUP_REPO` is planned to support full resource-level versioning, along the
+following lines:
+
+- A version for a resource may be created on demand at any point in time.
+- Each stored resource version is accessible via its own URI.
+- A resource can be restored to a previous version.
+- Versions of a resource may be collapsed into one (this is an irrevocable
+  action).
+- A resource can be soft-deleted, i.e. its current state is stored as a
+  version, and then replaced with a "tombstone" marker.
+- Any stored version of a soft-deleted resource can be accessed via its
+  tombstone.
+- A soft-deleted resource can be reinstated from any of its stored versions.
+- A soft-deleted resource or an active resource can be hard-deleted (i.e.
+  entirely and irrevocably removed from storage).
 
 ### Data resources
 

+ 35 - 0
doc/ROADMAP.md

@@ -0,0 +1,35 @@
+# `LSUP_REPO`: Simple road map
+
+P: Pending; W: working on it; T: testing; D: done
+
+- General repo
+  - *D* Initialize and tear down
+  - *D* Environment assets
+- DESC-R
+  - *D* Create
+  - *D* Store
+  - *W* Modify
+    - *W* Update (delta)
+  - *W* Retrieve from store
+  - *P* Versioning
+    - Create version
+    - Version listing and info
+    - Retrieve version
+    - Revert to version
+    - Squash versions (range)
+  - *P* Deletion
+    - Bury (tombstone)
+    - Exhume (from any version)
+    - Cremate (hard delete)
+  - *P* Data structures
+    - Proxies
+    - Sets
+    - Linked lists
+- *P* DATA-R
+  - Store
+  - Metadata (DESC-R) linking
+  - Versioning (see above)
+  - Deletion (see above)
+  - Versioning (see above)
+- *W* Tests
+- *P* Python bindings

+ 75 - 0
doc/examples.md

@@ -0,0 +1,75 @@
+# Examples
+
+## User input and storage layout for a newly created resource
+
+The following triples constitute a user data payload for a new resource,
+organized in named graphs.
+
+Note the relative URIs used for the subject, since the URI of the resource is
+unknown before creation:
+
+```
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix rsrc: <urn:lsres:> .
+@prefix ext: <http://example.org/onto#> .
+
+GRAPH <#usr1> {
+  <.>
+    a foaf:Person ;
+    foaf:knows <http://example.org/onto#bob> .
+}
+
+GRAPH <#usr2> {
+  <.>
+    foaf:nick "alice" ;
+    foaf:givenName "Alice" ;
+    foaf:familyName "Liddell" .
+}
+```
+
+Once the resource is created, the following triples are added to the repo,
+assuming that the new resource URI is `rsrc:abc` (in reality it would be a
+UUID4 formatted URN):
+
+```
+@prefix lsup: <urn:lsup:> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix rsrc: <urn:lsres:> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+GRAPH rsrc:abc#__admin {
+  <.>
+    a lsup:Resource , lsup:DescriptiveResource ;
+    lsup:created "2023-01-10T21:28:15Z"^^xsd:dateTime ;
+    lsup:lastUpdated "2023-01-10T21:28:15Z"^^xsd:dateTime .
+}
+
+GRAPH rsrc:abc#usr1 {
+  <.>
+    a foaf:Person ;
+    foaf:knows <http://example.org/onto#bob> .
+}
+
+GRAPH rsrc:abc#usr2 {
+  <.>
+    foaf:nick "alice" ;
+    foaf:givenName "Alice" ;
+    foaf:familyName "Liddell" .
+}
+
+GRAPH rsrc:abc#__main {
+  <#__admin>
+    a lsup:Metadata , lsup:AdminMetadata ;
+    foaf:topic <.> .
+  <#usr1>
+    a lsup:UserData ;
+    foaf:topic <.> .
+  <#usr2>
+    a lsup:UserData ;
+    foaf:topic <.> .
+}
+```
+
+That makes it easy to retrieve all triples about the created resource: the
+result is the union of all graphs whose URIs have `rsrc:abc` as their
+`foaf:topic`.

+ 28 - 2
include/desc.h

@@ -184,7 +184,21 @@ LSUP_Graph **
 LSR_desc_user_data (const LSR_Desc *rsrc);
 
 
-/** @brief Store a DESC-R, overwriting any data if it already exists.
+/** @brief All resource triples in a single graph.
+ *
+ * Return the union of all the user data and the admin metadata in one flat
+ * graph.
+ *
+ * @param[in] rsrc Resource handle.
+ *
+ * @return New graph with all the resource triples. The graph URI is the
+ * resource URN.
+ */
+LSUP_Graph *
+LSR_desc_triples (const LSR_Desc *rsrc);
+
+
+/** @brief Push an in-memory DESC-R into the persistent store.
  *
  * This is a "create or overwrite" function that deletes any existing resource
  * under the given ID before storing the content of the in-memory resource
@@ -200,7 +214,7 @@ LSR_desc_store (const LSR_Desc *rsrc);
 
 /** @brief Delete a DESC-R.
  *
- * TODO Soft-deletes to be implemented with versioning.
+ * TODO
  *
  * @param[in] id Resource ID.
  *
@@ -210,4 +224,16 @@ LSR_desc_store (const LSR_Desc *rsrc);
 LSUP_rc
 LSR_desc_delete (LSR_id id);
 
+
+/** @brief Get resource URN.
+ *
+ * @param[in] rsrc Resource pointer.
+ *
+ * @return New URI term. It must be freed with #LSUP_term_free() eventually.
+ */
+inline LSUP_Term *
+LSR_desc_urn (const LSR_Desc *rsrc)
+{ return LSR_id_to_urn (rsrc->id, NULL); }
+
+
 #endif /* _LSR_DESC_H */

+ 57 - 19
src/desc.c

@@ -15,7 +15,7 @@ LSR_desc_new_multi (LSUP_Graph *const *data, LSR_Desc **rsrc_p)
     LSUP_Term *main_data_urn = LSR_id_to_urn (rsrc->id, "__main");
     rsrc->main_data = LSUP_graph_new (NULL, main_data_urn, LSUP_default_nsm);
     LSUP_term_free (main_data_urn);
-    log_debug (
+    LOG_DEBUG (
             "Main data graph: %s",
             LSUP_graph_uri(rsrc->main_data)->data);
 
@@ -28,7 +28,7 @@ LSR_desc_new_multi (LSUP_Graph *const *data, LSR_Desc **rsrc_p)
     LSUP_Term *admin_uri = LSR_id_to_urn (rsrc->id, "__admin");
     rsrc->admin_data = LSUP_graph_new (NULL, admin_uri, NULL);
 
-    log_debug (
+    LOG_DEBUG (
             "Admin data graph (@%p): %s",
             LSUP_graph_uri (rsrc->admin_data),
             LSUP_graph_uri (rsrc->admin_data)->data);
@@ -141,14 +141,14 @@ LSR_desc_store (const LSR_Desc *rsrc)
     if (get_rc == LSUP_OK) {
         for (size_t i = 0; old_rsrc->user_data[i] != NULL; i++) {
 
-            LSUP_Term *gr_uri = LSUP_graph_uri (old_rsrc->user_data[i]);
+            const LSUP_Term *gr_uri = LSUP_graph_uri (old_rsrc->user_data[i]);
 
             size_t ct;
             // Remove triples from user graph.
             PCHECK (LSUP_graph_remove_txn (
                         txn, old_rsrc->user_data[i],
                         NULL, NULL, NULL, &ct), fail);
-            log_debug ("Removed %lu triples from graph %s", ct, gr_uri->data);
+            LOG_DEBUG ("Removed %lu triples from graph %s", ct, gr_uri->data);
 
             // Remove user graph metadata.
             PCHECK (LSUP_graph_remove_txn (
@@ -157,8 +157,6 @@ LSR_desc_store (const LSR_Desc *rsrc)
             PCHECK (LSUP_graph_remove_txn (
                         txn, old_rsrc->main_data,
                         NULL, NULL, gr_uri, NULL), fail);
-
-            LSUP_term_free (gr_uri);
         }
     }
 
@@ -173,7 +171,7 @@ LSR_desc_store (const LSR_Desc *rsrc)
             goto fail;
         }
         PCHECK (LSUP_graph_copy_contents_txn (
-                    txn, rsrc->user_data[i], tmp_gr), fail);
+                    txn, rsrc->user_data[i], tmp_gr, NULL, NULL, NULL), fail);
         LSUP_graph_free (tmp_gr);
     }
 
@@ -185,7 +183,7 @@ LSR_desc_store (const LSR_Desc *rsrc)
         goto fail;
     }
     PCHECK (LSUP_graph_copy_contents_txn (
-                txn, rsrc->admin_data, tmp_gr), fail);
+                txn, rsrc->admin_data, tmp_gr, NULL, NULL, NULL), fail);
     LSUP_graph_free (tmp_gr);
 
     // Update graph metadata.
@@ -196,7 +194,7 @@ LSR_desc_store (const LSR_Desc *rsrc)
         goto fail;
     }
     PCHECK (LSUP_graph_copy_contents_txn (
-                txn, rsrc->main_data, tmp_gr), fail);
+                txn, rsrc->main_data, tmp_gr, NULL, NULL, NULL), fail);
     LSUP_graph_free (tmp_gr);
 
     PCHECK (LSUP_store_commit (LSR_store, txn), fail);
@@ -213,6 +211,14 @@ fail:
 }
 
 
+LSUP_rc
+LSR_desc_delete (LSR_id id)
+{
+    // TODO
+    return LSUP_NOT_IMPL_ERR;
+}
+
+
 LSUP_rc
 LSR_desc_get (const uuid_t id, LSR_Desc **rsrc_p)
 {
@@ -273,7 +279,7 @@ LSR_desc_metadata (const LSR_Desc *rsrc)
 {
     LSUP_Graph *res = LSUP_graph_new (
             LSR_store, LSUP_graph_uri (rsrc->admin_data), NULL);
-    LSUP_graph_copy_contents (rsrc->admin_data, res);
+    LSUP_graph_copy (rsrc->admin_data, res);
 
     return res;
 }
@@ -283,19 +289,43 @@ LSUP_Graph **
 LSR_desc_user_data (const LSR_Desc *rsrc)
 {
     size_t ct = 0;
-    while (rsrc->user_data[ct++]);
+    while (rsrc->user_data[ct]) ct++;
     LSUP_Graph **res = malloc (sizeof (*res) * (ct + 1));
     if (UNLIKELY (!res)) return NULL;
 
     for (size_t i = 0; i < ct; i++) {
         res[i] = LSUP_graph_new (
                 LSR_store, LSUP_graph_uri (rsrc->user_data[i]), NULL);
-        LSUP_graph_copy_contents (rsrc->user_data[i], res[i]);
+        PCHECK (LSUP_graph_copy (rsrc->user_data[i], res[i]), fail);
     }
-
     res[ct] = NULL;
 
     return res;
+
+fail:
+    for (size_t i = 0; i < ct; i++)
+        LSUP_graph_free (res[i]);
+    return NULL;
+}
+
+
+LSUP_Graph *
+LSR_desc_triples (const LSR_Desc *rsrc)
+{
+    LSUP_Graph *res = LSR_desc_metadata (rsrc);
+    if (UNLIKELY (!res)) return NULL;
+    LSUP_graph_set_uri (res, LSR_desc_urn (rsrc));
+
+    size_t ct = 0;
+    while (rsrc->user_data[ct]) ct++;
+    for (size_t i = 0; i < ct; i++)
+        PCHECK (LSUP_graph_copy (rsrc->user_data[i], res), fail);
+
+    return res;
+
+fail:
+    LSUP_graph_free (res);
+    return NULL;
 }
 
 
@@ -331,7 +361,7 @@ LSR_desc_update (
             for (j = 0; j < 4; j++)
                 LSUP_buffer_free (sspoc[j]);
 
-            log_debug (
+            LOG_DEBUG (
                     "Removed %lu triples for %s",
                     ct_loop, rm_data[i][0]->data);
 
@@ -365,9 +395,9 @@ LSR_desc_update (
 
     // Loop over input graphs.
     for (size_t i = 0; i < ct; i++) {
-        LSUP_Term *gr_uri = LSUP_graph_uri (add_data[i]);
+        const LSUP_Term *gr_uri = LSUP_graph_uri (add_data[i]);
 
-        log_debug ("Adding user data #%lu with uri: %s", i, gr_uri->data);
+        LOG_DEBUG ("Adding user data #%lu with uri: %s", i, gr_uri->data);
 
         LSUP_Term *rel_uri = LSUP_iriref_relative (rsrc_uri, gr_uri);
         if (strstr (rel_uri->data, "#__") == rel_uri->data) {
@@ -378,7 +408,7 @@ LSR_desc_update (
         if (rc < 0) goto finally;
 
         rsrc->user_data[i] = LSUP_graph_new (NULL, gr_uri, NULL);
-        log_debug (
+        LOG_DEBUG (
                 "User data graph (@%p): %s",
                 LSUP_graph_uri(rsrc->user_data[i]),
                 LSUP_graph_uri(rsrc->user_data[i])->data);
@@ -449,7 +479,7 @@ LSR_desc_update (
                             strcmp (spo->o->data, "lsup:ListItem") == 0 ||
                             strcmp (spo->o->data, "lsup:Set") == 0 ||
                             strcmp (spo->o->data, "lsup:Proxy") == 0) {
-                        // TODO
+                        // TODO handle system-managed types.
                     } else {
                         log_error (
                                 "%s is a managed predicate and cannot "
@@ -474,7 +504,7 @@ loop_end:
         // Add user graph metadata to default graph.
         LSUP_GraphIterator *admin_add_it = LSUP_graph_add_init (
                 rsrc->main_data);
-        dest_s = gr_uri;
+        dest_s = LSUP_iriref_new (gr_uri->data, NULL);
         dest_p = LSUP_iriref_new ("rdf:type", LSUP_default_nsm);
 
         dest_o = LSUP_iriref_new ("lsup:Metadata", LSUP_default_nsm);
@@ -495,6 +525,8 @@ loop_end:
         LSUP_graph_add_iter (admin_add_it, spo);
         LSUP_term_free (dest_p);
 
+        LSUP_term_free (dest_s);
+
         LSUP_graph_iter_free (lu_it);
         LSUP_graph_add_done (add_it);
         LSUP_graph_add_done (admin_add_it);
@@ -510,3 +542,9 @@ finally:
 }
 
 
+/*
+ * Inline function declarations.
+ */
+
+LSUP_Term *LSR_desc_urn (const LSR_Desc *rsrc);
+

+ 6 - 2
src/repo_core.c

@@ -117,7 +117,7 @@ LSUP_rc LSR_init (void)
             sizeof (LSUP_Term), 0, LSUP_HASH_SEED, 0,
             tmap_hash_fn, tmap_cmp_fn, tmap_free_fn, NULL);
     for (int i = 0; mgd_pred_str[i] != NULL; i++) {
-        log_trace ("Storing managed predicate: %s", mgd_pred_str[i]);
+        LOG_TRACE ("Storing managed predicate: %s", mgd_pred_str[i]);
         LSUP_Term *uri = LSUP_iriref_new (mgd_pred_str[i], LSUP_default_nsm);
 
         hashmap_set (LSR_managed_preds, uri);
@@ -129,7 +129,7 @@ LSUP_rc LSR_init (void)
             sizeof (LSUP_Term), 0, LSUP_HASH_SEED, 0,
             tmap_hash_fn, tmap_cmp_fn, tmap_free_fn, NULL);
     for (int i = 0; mgd_type_str[i] != NULL; i++) {
-        log_trace ("Storing managed type: %s", mgd_type_str[i]);
+        LOG_TRACE ("Storing managed type: %s", mgd_type_str[i]);
         LSUP_Term *uri = LSUP_iriref_new (
                 mgd_type_str[i], LSUP_default_nsm);
 
@@ -163,4 +163,8 @@ void LSR_done (void)
 }
 
 
+/*
+ * Inline function declarations.
+ */
+
 LSUP_Term *LSR_id_to_urn (const uuid_t id, const char *frag);

+ 37 - 2
test/test_desc.c

@@ -30,6 +30,7 @@ test_desc_create()
 
     free(err);
 
+    // Create resource.
     LSR_Desc *rsrc;
     EXPECT_PASS (LSR_desc_new_multi (data, &rsrc));
 
@@ -38,15 +39,49 @@ test_desc_create()
     LSUP_graph_free (data[0]);
     LSUP_graph_free (data[1]);
 
+    LSUP_Graph *md_gr = LSR_desc_metadata(rsrc);
+    ASSERT (md_gr != NULL, "Error retrieving metadata graph!");
+    ASSERT (
+            LSUP_term_equals (
+                    LSUP_graph_uri (md_gr), LSUP_graph_uri (rsrc->admin_data)),
+            "Metadata function URI is incorrect!");
+
+    ASSERT (sizeof(md_gr) > 0, "No metadata found for resource!");
+
+
+    LSUP_Term
+        *rsrc_urn = LSR_desc_urn (rsrc),
+        *main_urn = LSR_id_to_urn (rsrc->id, "__main"),
+        *admin_urn = LSR_id_to_urn (rsrc->id, "__admin"),
+        *usr1_urn = LSR_id_to_urn (rsrc->id, "usr1"),
+        *usr2_urn = LSR_id_to_urn (rsrc->id, "usr2");
+
     // TODO tests:
     // * Existence of admin graph
     // * Existence of main graph
-    // * Linkage of admin and main graphs
     // * Existence of user graphs
-    // * Linkage of user graphs
+    // * Linkage of main, admin, and user graphs
     // * Size of user graphs
     // * Spot-test triples in user graphs
 
+    ASSERT (
+            LSUP_term_equals (main_urn, LSUP_graph_uri (rsrc->main_data)),
+            "Main graph URI is incorrect!");
+
+    ASSERT (
+            LSUP_term_equals (admin_urn, LSUP_graph_uri (md_gr)),
+            "Admin graph URI is incorrect!");
+
+    // TODO
+
+    LSUP_term_free (usr2_urn);
+    LSUP_term_free (usr1_urn);
+    LSUP_term_free (admin_urn);
+    LSUP_term_free (main_urn);
+    LSUP_term_free (rsrc_urn);
+
+    LSUP_graph_free (md_gr);
+
     // Store for next test before freeing.
     EXPECT_PASS (LSR_desc_store (rsrc));