2 Commits 3f1569f63e ... c6c1387ebb

Author SHA1 Message Date
  scossu c6c1387ebb WIP Separate update function. 1 year ago
  scossu 5765a1e558 WIP 1 year ago
6 changed files with 309 additions and 229 deletions
  1. 2 1
      Makefile
  2. 3 0
      include/core.h
  3. 25 0
      include/desc.h
  4. 6 0
      src/core.c
  5. 255 216
      src/desc.c
  6. 18 12
      test/test_desc.c

+ 2 - 1
Makefile

@@ -88,9 +88,10 @@ debug_install: install debug ## Install default and debug libraries.
 	cp liblsuprdf_dbg.{a,so} $(DESTDIR)$(libdir)
 	
 
+
 .PHONY: clean
 clean:
-	rm -rf src/*.[aod] ./*[aod]
+	rm -rf src/*.[aod] ./*.{a,o,d,so}
 	rm -f bin/test*
 
 

+ 3 - 0
include/core.h

@@ -61,6 +61,9 @@ extern LSR_TermMap *LSR_managed_types;
 /// Default back end store.
 extern LSUP_Store *LSR_store;
 
+/// RDF type URI.
+extern LSUP_Term *LSR_rdf_t;
+
 
 /*
  * API.

+ 25 - 0
include/desc.h

@@ -119,6 +119,31 @@ LSUP_rc
 LSR_desc_get (const uuid_t id, LSR_Desc **rsrc);
 
 
+
+/** @brief Update an existing resource.
+ *
+ * @param[in] rsrc Resource handle.
+ *
+ * @param[in] rm_data NULL-terminated array of graph handles. Each graph
+ * contains triples to be removed from graphs with a matching URI in the
+ * resource. If no matching graph is found, that graph has no effect.
+ * Note that some internal functions may pass NULL to this parameter. This
+ * should not be done externally because it assumes that the resource is
+ * new and may leave it in an inconsistent state. This is different from
+ * passing `{NULL}`.
+ *
+ * @param[in] add_data NULL-terminated array of graph handles to add. If the
+ * resource has already a matching graph for a graph being added, the triples
+ * are added to the existing graph.
+ *
+ * @return LSUP_OK on successful update; TODO
+ */
+LSUP_rc
+LSR_desc_update (
+        LSR_Desc *rsrc, LSUP_Graph *const *rm_data,
+        LSUP_Graph *const *add_data);
+
+
 /** @brief Free a DESC-R.
  */
 void

+ 6 - 0
src/core.c

@@ -3,6 +3,8 @@
 
 bool LSR_is_init = false;
 
+LSUP_Term *LSR_rdf_t = NULL;
+
 /*
  * Initial namespace map.
  */
@@ -87,6 +89,8 @@ LSUP_rc LSR_init (void)
     LSUP_rc rc = LSUP_init();
     if (rc < 0) return rc;
 
+    LSR_rdf_t = LSUP_iriref_new ("rdf:type", LSUP_default_nsm);
+
     // Set up default back end.
     // If LSR_BACKEND_URN is not defined in env, LSUP_MDB_STORE_URN is used;
     // if that is also not set, lsup_rdf will use its default—which may be in
@@ -151,6 +155,8 @@ void LSR_done (void)
 
     hashmap_free (LSR_managed_preds);
     hashmap_free (LSR_managed_types);
+    LSUP_term_free (LSR_rdf_t);
+    LSR_rdf_t = NULL;
 
     LSUP_done();
     LSR_is_init = false;

+ 255 - 216
src/desc.c

@@ -1,5 +1,6 @@
 #include "desc.h"
 
+
 LSUP_rc
 LSR_desc_new_multi (LSUP_Graph *const *data, LSR_Desc **rsrc_p)
 {
@@ -11,234 +12,89 @@ LSR_desc_new_multi (LSUP_Graph *const *data, LSR_Desc **rsrc_p)
     uuid_generate_random (rsrc->id);
 
     // Default context graph.
-    rsrc->main_data = LSUP_graph_new (NULL, LSUP_default_ctx, NULL);
+    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 (
             "Main data graph: %s",
             LSUP_graph_uri(rsrc->main_data)->data);
 
-    LSUP_GraphIterator
-        *lu_it,                 // Lookup iterator.
-        *add_it,                // Main graph add iterator.
-        *admin_add_it = NULL;   // Admin graph add iterator.
-
-    LSUP_Term *dest_s, *dest_p, *dest_o;
-    LSUP_Triple src_spo_s, dest_spo_s;
-    LSUP_Triple
-        *src_spo = &src_spo_s,
-        *dest_spo = &dest_spo_s;
+    PCHECK (LSR_desc_update (rsrc, NULL, data), finally);
 
     LSUP_Term *rsrc_uri = LSR_id_to_urn (rsrc->id, NULL);
-    LSUP_Term *rdf_t = LSUP_iriref_new ("rdf:type", LSUP_default_nsm);
-
-    // Count graphs inserted and allocate space.
-    size_t ct = 0;
-    while (data[ct]) ct++;
-    rsrc->user_data = calloc (sizeof (*rsrc->user_data), ct + 1);
-    if (UNLIKELY (! rsrc->user_data)) return LSUP_MEM_ERR;
-
-    /* BEGIN adding user data. */
-
-    // Loop over input graphs.
-    for (size_t i = 0; i < ct; i++) {
-        LSUP_Term *gr_uri = LSUP_graph_uri (data[i]);
-
-        LSUP_Term *rel_uri = LSUP_iriref_relative (rsrc_uri, gr_uri);
-        if (strstr (rel_uri->data, "#__") == rel_uri->data) {
-            log_error ("Fragment URI cannot start with double underscore.");
-            rc = LSUP_VALUE_ERR;
-        }
-        LSUP_term_free (rel_uri);
-        if (rc < 0) goto finally;
-
-        rsrc->user_data[i] = LSUP_graph_new (NULL, gr_uri, NULL);
-        log_debug (
-                "User data graph (@%p): %s",
-                LSUP_graph_uri(rsrc->user_data[i]),
-                LSUP_graph_uri(rsrc->user_data[i])->data);
-
-        add_it = LSUP_graph_add_init (rsrc->user_data[i]);
-        lu_it = LSUP_graph_lookup (rsrc->user_data[i], NULL, NULL, NULL, NULL);
-
-        // Loop over graph triples.
-        while (LSUP_graph_iter_next (lu_it, &src_spo) == LSUP_OK) {
-            dest_s = LSUP_IS_IRI (src_spo->s) ?
-                    LSUP_iriref_relative (rsrc_uri, src_spo->s) : src_spo->s;
-            dest_p = LSUP_term_copy (src_spo->p);
-            dest_o = LSUP_IS_IRI (src_spo->s) ?
-                    LSUP_iriref_relative (rsrc_uri, src_spo->s) : src_spo->s;
-            LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o);
-
-            // if the pred is managed, ignore the triple and send a warning.
-            if (hashmap_get(LSR_managed_preds, dest_spo->p)) {
-                log_warn (
-                        "Predicate %s is managed. Skipping triple.",
-                        dest_p->data);
-                goto loop_end;
-            }
-
-            /*
-             * If the subject or object is a resource, check if it exists; if
-             * it does, add triple to user_data; if not, return an error.
-             */
-            uuid_t id_tmp;
-            LSUP_rc tmp_rc;
-            // Check subject.
-            if (LSR_IS_RSRC_IRI (dest_s)) {
-                uuid_parse (dest_s->data + strlen (LSR_RSRC_NS), id_tmp);
-                tmp_rc = LSR_desc_get (id_tmp, NULL);
-                if (tmp_rc != LSUP_OK) {
-                    log_error (
-                            "Referenced subject does not exist: %s",
-                            dest_s->data + strlen (LSR_RSRC_NS));
-                    rc = LSUP_VALUE_ERR;
-                    goto finally;
-                }
-            }
-            // Check object.
-            if (LSR_IS_RSRC_IRI (dest_o)) {
-                uuid_parse (dest_o->data + strlen (LSR_RSRC_NS), id_tmp);
-                tmp_rc = LSR_desc_get (id_tmp, NULL);
-                if (tmp_rc != LSUP_OK) {
-                    log_error (
-                            "Referenced object does not exist: %s",
-                            dest_o->data + strlen (LSR_RSRC_NS));
-                    rc = LSUP_VALUE_ERR;
-                    goto finally;
-                }
-            }
-
-            // RDF type check.
-            if (
-                LSUP_term_equals (
-                    gr_uri, LSUP_iriref_absolute (rsrc_uri, dest_spo->s))
-                && LSUP_term_equals (rdf_t, dest_spo->p)
-            ) {
-                // If the resource is a special type, handle specific workflow.
-                // TODO
-                if (hashmap_get (LSR_managed_types, dest_spo->o)) {
-                }
-            }
-
-            // Add triple to user_data.
-            LSUP_graph_add_iter (add_it, dest_spo);
-loop_end:
-            if (dest_s != src_spo->s) LSUP_term_free (dest_s);
-            if (dest_o != src_spo->o) LSUP_term_free (dest_o);
-        }
-
-        // Add user graph metadata to default graph.
-        admin_add_it = LSUP_graph_add_init (rsrc->main_data);
-        dest_s = gr_uri;
-        dest_p = LSUP_iriref_new ("rdf:type", LSUP_default_nsm);
-
-        dest_o = LSUP_iriref_new ("lsup:Metadata", LSUP_default_nsm);
-        LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o);
-        LSUP_graph_add_iter (admin_add_it, dest_spo);
-        LSUP_term_free (dest_o);
-
-        dest_o = LSUP_iriref_new ("lsup:UserMetadata", LSUP_default_nsm);
-        LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o);
-        LSUP_graph_add_iter (admin_add_it, dest_spo);
-        LSUP_term_free (dest_o);
-        LSUP_term_free (dest_p);
-
-        // Relationship between data graph and resource.
-        dest_p = LSUP_iriref_new ("foaf:primaryTopic", LSUP_default_nsm);
-        dest_o = rsrc_uri;
-        LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o);
-        LSUP_graph_add_iter (admin_add_it, dest_spo);
-        LSUP_term_free (dest_p);
-
-        LSUP_graph_iter_free (lu_it);
-        LSUP_graph_add_done (add_it);
-        LSUP_graph_add_done (admin_add_it);
-        lu_it = add_it = admin_add_it = NULL;
-    }
-
-    /* END adding user data. */
 
     /* BEGIN adding managed (admin) data. */
 
-    LSUP_Term *gr_uri = LSR_id_to_urn (rsrc->id, "__admin");
-    rsrc->admin_data = LSUP_graph_new (NULL, gr_uri, NULL);
+    LSUP_Term *admin_uri = LSR_id_to_urn (rsrc->id, "__admin");
+    rsrc->admin_data = LSUP_graph_new (NULL, admin_uri, NULL);
+
     log_debug (
             "Admin data graph (@%p): %s",
-            LSUP_graph_uri(rsrc->admin_data),
-            LSUP_graph_uri(rsrc->admin_data)->data);
-
-    admin_add_it = LSUP_graph_add_init (rsrc->admin_data);
-    dest_s = LSUP_iriref_new("", NULL); // Relative to resource URI.
-    LSUP_Triple admin_spo_s;
-    LSUP_Triple *admin_spo = &admin_spo_s;
+            LSUP_graph_uri (rsrc->admin_data),
+            LSUP_graph_uri (rsrc->admin_data)->data);
 
-    // RDF types.
-    dest_p = rdf_t;
-
-    dest_o = LSUP_iriref_new ("lsup:Resource", LSUP_default_nsm);
-    LSUP_triple_init (admin_spo, dest_s, dest_p, dest_o);
-    LSUP_graph_add_iter (admin_add_it, admin_spo);
-    LSUP_term_free (dest_o);
-
-    dest_o = LSUP_iriref_new ("lsup:DescriptiveResource", LSUP_default_nsm);
-    LSUP_triple_init (admin_spo, dest_s, dest_p, dest_o);
-    LSUP_graph_add_iter (admin_add_it, admin_spo);
-    LSUP_term_free (dest_o);
-
-    // Timestamps. For now, second precision is fine.
+    // Calculate timestamp.
     time_t now;
     time (&now);
-    char buf [sizeof ("0000-00-00T00:00:00Z")];
-    strftime (buf, sizeof (buf), "%FT%TZ", gmtime (&now));
-
-    dest_p = LSUP_iriref_new ("lsup:created", LSUP_default_nsm);
-    dest_o = LSUP_literal_new (
-            buf, LSUP_iriref_new ("xsd:dateTime", LSUP_default_nsm));
-    LSUP_triple_init (admin_spo, dest_s, dest_p, dest_o);
-    LSUP_graph_add_iter (admin_add_it, admin_spo);
-    LSUP_term_free (dest_p);
-
-    dest_p = LSUP_iriref_new ("lsup:lastModified", LSUP_default_nsm);
-    LSUP_triple_init (admin_spo, dest_s, dest_p, dest_o);
-    LSUP_graph_add_iter (admin_add_it, admin_spo);
-    LSUP_term_free (dest_p);
-    LSUP_term_free (dest_o);
-    LSUP_graph_add_done (admin_add_it);
+    char tstamp [sizeof ("0000-00-00T00:00:00Z")];
+    strftime (tstamp, sizeof (tstamp), "%FT%TZ", gmtime (&now));
+
+    LSUP_Term
+        *s = LSUP_iriref_new("", NULL), // Relative to resource URI.
+        *created_t = LSUP_iriref_new ("lsup:created", LSUP_default_nsm),
+        *rsrc_t = LSUP_iriref_new ("lsup:Resource", LSUP_default_nsm),
+        *desc_rsrc_t = LSUP_iriref_new (
+                "lsup:DescriptiveResource", LSUP_default_nsm),
+        *ts_t = LSUP_literal_new (
+                tstamp, LSUP_iriref_new ("xsd:dateTime", LSUP_default_nsm));
+
+    LSUP_Triple *admin_trp[] = {
+        LSUP_triple_new (s, LSR_rdf_t, rsrc_t),
+        LSUP_triple_new (s, LSR_rdf_t, desc_rsrc_t),
+        LSUP_triple_new (s, created_t, ts_t),
+        NULL,
+    };
+
+    rc = LSUP_graph_add (rsrc->admin_data, admin_trp, NULL);
+    PCHECK (rc, finally);
+
+    for (size_t i = 0; admin_trp[i]; i++)
+        free (admin_trp[i]);
+    LSUP_term_free (s);
+    LSUP_term_free (created_t);
+    LSUP_term_free (rsrc_t);
+    LSUP_term_free (desc_rsrc_t);
+    LSUP_term_free (ts_t);
 
     /* END adding admin data. */
 
     /* BEGIN adding graph metadata (main). */
 
-    admin_add_it = LSUP_graph_add_init (rsrc->main_data);
-    LSUP_term_free (dest_s);
-    dest_s = gr_uri;
-    dest_p = rdf_t;
-
-    dest_o = LSUP_iriref_new ("lsup:Metadata", LSUP_default_nsm);
-    LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o);
-    LSUP_graph_add_iter (admin_add_it, dest_spo);
-    LSUP_term_free (dest_o);
-
-    dest_o = LSUP_iriref_new ("lsup:AdminMetadata", LSUP_default_nsm);
-    LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o);
-    LSUP_graph_add_iter (admin_add_it, dest_spo);
-    LSUP_term_free (dest_o);
-
-    // Relationship between data graph and resource.
-    dest_p = LSUP_iriref_new ("foaf:primaryTopic", LSUP_default_nsm);
-    dest_o = rsrc_uri;
-    LSUP_triple_init (dest_spo, dest_s, dest_p, dest_o);
-    LSUP_graph_add_iter (admin_add_it, dest_spo);
-    LSUP_term_free (dest_p);
-    LSUP_term_free (dest_s); // === gr_uri
-
-    LSUP_graph_add_done (admin_add_it);
-    admin_add_it = NULL;
+    LSUP_Term
+            *meta_t = LSUP_iriref_new ("lsup:Metadata", LSUP_default_nsm),
+            *admin_meta_t = LSUP_iriref_new (
+                    "lsup:AdminMetadata", LSUP_default_nsm),
+            *topic_t = LSUP_iriref_new ("foaf:primaryTopic", LSUP_default_nsm);
+    LSUP_Triple *main_trp[] = {
+        LSUP_triple_new (admin_uri, LSR_rdf_t, meta_t),
+        LSUP_triple_new (admin_uri, LSR_rdf_t, admin_meta_t),
+        LSUP_triple_new (admin_uri, topic_t, rsrc_uri),
+        NULL
+    };
+
+    rc = LSUP_graph_add (rsrc->main_data, main_trp, NULL);
+
+    for (size_t i = 0; main_trp[i]; i++)
+        free (main_trp[i]);
+    LSUP_term_free (meta_t);
+    LSUP_term_free (admin_meta_t);
+    LSUP_term_free (topic_t);
 
     /* END adding graph metadata. */
 
 finally:
     LSUP_term_free (rsrc_uri);
-    LSUP_term_free (rdf_t);
+    LSUP_term_free (admin_uri);
 
     if (rc < 0) goto fail;
 
@@ -249,9 +105,6 @@ finally:
     return rc;
 
 fail:
-    LSUP_graph_iter_free (lu_it);
-    LSUP_graph_add_done (add_it);
-    LSUP_graph_add_done (admin_add_it);
     LSR_desc_free (rsrc);
     *rsrc_p = NULL;
 
@@ -259,10 +112,22 @@ fail:
 }
 
 
+void LSR_desc_free (LSR_Desc *rsrc)
+{
+    size_t i = 0;
+    while (rsrc->user_data[i])
+        LSUP_graph_free (rsrc->user_data[i++]);
+    free (rsrc->user_data);
+
+    LSUP_graph_free (rsrc->admin_data);
+    LSUP_graph_free (rsrc->main_data);
+    free (rsrc);
+}
+
+
 LSUP_rc
 LSR_desc_store (const LSR_Desc *rsrc)
 {
-    // TODO Make atomic. Needs to implement transactions in backend.
     LSR_Desc *old_rsrc;
     PRCCK (LSR_desc_get (rsrc->id, &old_rsrc));
 
@@ -439,16 +304,190 @@ LSR_desc_user_data (const LSR_Desc *rsrc)
 }
 
 
-void LSR_desc_free (LSR_Desc *rsrc)
+LSUP_rc
+LSR_desc_update (
+        LSR_Desc *rsrc, LSUP_Graph *const *rm_data,
+        LSUP_Graph *const *add_data)
 {
-    size_t i = 0;
-    while (rsrc->user_data[i])
-        LSUP_graph_free (rsrc->user_data[i++]);
-    free (rsrc->user_data);
+    LSUP_rc rc = LSUP_NOACTION;
+    LSUP_Term *rsrc_uri = LSR_id_to_urn (rsrc->id, NULL);
 
-    LSUP_graph_free (rsrc->admin_data);
-    LSUP_graph_free (rsrc->main_data);
-    free (rsrc);
+    size_t ct;
+
+    /*
+     * REMOVE user data.
+     */
+
+    if (rm_data) {
+        // Count graphs to be removed.
+        ct = 0;
+        while (add_data[ct]) ct++;
+        for (size_t i = 0; i < ct; i++) {
+            LSUP_Term *gr_uri = LSUP_graph_uri (rm_data[i]);
+            // TODO remove ops.
+            // TODO if graph is empty after removal, remove it.
+            LSUP_term_free (gr_uri);
+        }
+    }
+
+    /*
+     * ADD user data.
+     */
+
+    // Count graphs inserted and allocate space.
+    ct = 0;
+    while (add_data[ct]) ct++;
+    rsrc->user_data = calloc (sizeof (*rsrc->user_data), ct + 1);
+    if (UNLIKELY (! rsrc->user_data)) return LSUP_MEM_ERR;
+
+    LSUP_Triple spo_s;
+    LSUP_Triple *spo = &spo_s;
+    LSUP_GraphIterator
+        *lu_it,                 // Lookup iterator.
+        *add_it;                // Main graph add iterator.
+
+    // Loop over input graphs.
+    for (size_t i = 0; i < ct; i++) {
+        LSUP_Term *gr_uri = LSUP_graph_uri (add_data[i]);
+
+        LSUP_Term *rel_uri = LSUP_iriref_relative (rsrc_uri, gr_uri);
+        if (strstr (rel_uri->data, "#__") == rel_uri->data) {
+            log_error ("Fragment URI cannot start with double underscore.");
+            rc = LSUP_VALUE_ERR;
+        }
+        LSUP_term_free (rel_uri);
+        if (rc < 0) goto finally;
+
+        rsrc->user_data[i] = LSUP_graph_new (NULL, gr_uri, NULL);
+        log_debug (
+                "User data graph (@%p): %s",
+                LSUP_graph_uri(rsrc->user_data[i]),
+                LSUP_graph_uri(rsrc->user_data[i])->data);
+
+        add_it = LSUP_graph_add_init (rsrc->user_data[i]);
+        lu_it = LSUP_graph_lookup (rsrc->user_data[i], NULL, NULL, NULL, NULL);
+
+        // Loop over graph triples.
+        LSUP_Term *dest_s, *dest_p, *dest_o;
+        LSUP_Triple *src_spo;
+        while (LSUP_graph_iter_next (lu_it, &src_spo) == LSUP_OK) {
+            dest_s = LSUP_IS_IRI (src_spo->s) ?
+                    LSUP_iriref_relative (rsrc_uri, src_spo->s) : src_spo->s;
+            dest_p = LSUP_term_copy (src_spo->p);
+            dest_o = LSUP_IS_IRI (src_spo->s) ?
+                    LSUP_iriref_relative (rsrc_uri, src_spo->s) : src_spo->s;
+            LSUP_triple_init (spo, dest_s, dest_p, dest_o);
+
+            // if the pred is managed, ignore the triple and send a warning.
+            if (hashmap_get(LSR_managed_preds, spo->p)) {
+                log_warn (
+                        "Predicate %s is managed. Skipping triple.",
+                        dest_p->data);
+                goto loop_end;
+            }
+
+            /*
+             * If the subject or object is a resource, check if it exists; if
+             * it does, add triple to user_data; if not, return an error.
+             */
+            uuid_t id_tmp;
+            LSUP_rc tmp_rc;
+            // Check subject.
+            if (LSR_IS_RSRC_IRI (dest_s)) {
+                uuid_parse (dest_s->data + strlen (LSR_RSRC_NS), id_tmp);
+                tmp_rc = LSR_desc_get (id_tmp, NULL);
+                if (tmp_rc != LSUP_OK) {
+                    log_error (
+                            "Referenced subject does not exist: %s",
+                            dest_s->data);
+                    rc = LSUP_VALUE_ERR;
+                    goto finally;
+                }
+            }
+            // Check object.
+            if (LSR_IS_RSRC_IRI (dest_o)) {
+                uuid_parse (dest_o->data + strlen (LSR_RSRC_NS), id_tmp);
+                tmp_rc = LSR_desc_get (id_tmp, NULL);
+                if (tmp_rc != LSUP_OK) {
+                    log_error (
+                            "Referenced object does not exist: %s",
+                            dest_o->data + strlen (LSR_RSRC_NS));
+                    rc = LSUP_VALUE_ERR;
+                    goto finally;
+                }
+            }
+
+            // RDF type check.
+            if (
+                LSUP_term_equals (
+                    gr_uri, LSUP_iriref_absolute (rsrc_uri, spo->s))
+                && LSUP_term_equals (LSR_rdf_t, spo->p)
+            ) {
+                // If the resource is a special type, handle specific workflow.
+                if (hashmap_get (LSR_managed_types, spo->o)) {
+                    if (
+                            strcmp (spo->o->data, "lsup:List") == 0 ||
+                            strcmp (spo->o->data, "lsup:ListItem") == 0 ||
+                            strcmp (spo->o->data, "lsup:Set") == 0 ||
+                            strcmp (spo->o->data, "lsup:Proxy") == 0) {
+                        // TODO
+                    } else {
+                        log_error (
+                                "%s is a managed predicate and cannot "
+                                "be used on creation.", spo->o->data);
+                        rc = LSUP_VALUE_ERR;
+                        goto finally;
+                    }
+                }
+            }
+
+            // Add triple to user_data.
+            LSUP_graph_add_iter (add_it, spo);
+loop_end:
+            if (dest_s != src_spo->s) LSUP_term_free (dest_s);
+            if (dest_o != src_spo->o) LSUP_term_free (dest_o);
+        }
+
+        /*
+         * UPDATE graph metadata.
+         */
+
+        // Add user graph metadata to default graph.
+        LSUP_GraphIterator *admin_add_it = LSUP_graph_add_init (
+                rsrc->main_data);
+        dest_s = gr_uri;
+        dest_p = LSUP_iriref_new ("rdf:type", LSUP_default_nsm);
+
+        dest_o = LSUP_iriref_new ("lsup:Metadata", LSUP_default_nsm);
+        LSUP_triple_init (spo, dest_s, dest_p, dest_o);
+        LSUP_graph_add_iter (admin_add_it, spo);
+        LSUP_term_free (dest_o);
+
+        dest_o = LSUP_iriref_new ("lsup:UserMetadata", LSUP_default_nsm);
+        LSUP_triple_init (spo, dest_s, dest_p, dest_o);
+        LSUP_graph_add_iter (admin_add_it, spo);
+        LSUP_term_free (dest_o);
+        LSUP_term_free (dest_p);
+
+        // Relationship between data graph and resource.
+        dest_p = LSUP_iriref_new ("foaf:primaryTopic", LSUP_default_nsm);
+        dest_o = rsrc_uri;
+        LSUP_triple_init (spo, dest_s, dest_p, dest_o);
+        LSUP_graph_add_iter (admin_add_it, spo);
+        LSUP_term_free (dest_p);
+
+        LSUP_graph_iter_free (lu_it);
+        LSUP_graph_add_done (add_it);
+        LSUP_graph_add_done (admin_add_it);
+        lu_it = add_it = admin_add_it = NULL;
+    }
+
+finally:
+    LSUP_term_free (rsrc_uri);
+    if (lu_it) LSUP_graph_iter_free (lu_it);
+    if (add_it) LSUP_graph_add_done (add_it);
+
+    return rc;
 }
 
 

+ 18 - 12
test/test_desc.c

@@ -21,19 +21,19 @@ test_desc_create()
         LSUP_iriref_new ("urn:o:20", NULL),
     };
 
-    LSUP_Triple trp1[] = {
-        {terms1[0], terms1[2], terms1[4]},
-        {terms1[0], terms1[3], terms1[4]},
-        {terms1[0], terms1[3], terms1[5]},
-        {terms1[1], terms1[2], terms1[4]},
-        {NULL}
+    LSUP_Triple *trp1[] = {
+        LSUP_triple_new (terms1[0], terms1[2], terms1[4]),
+        LSUP_triple_new (terms1[0], terms1[3], terms1[4]),
+        LSUP_triple_new (terms1[0], terms1[3], terms1[5]),
+        LSUP_triple_new (terms1[1], terms1[2], terms1[4]),
+        NULL
     };
-    LSUP_Triple trp2[] = {
-        {terms2[0], terms2[2], terms2[4]},
-        {terms2[0], terms2[3], terms2[4]},
-        {terms2[0], terms2[3], terms2[5]},
-        {terms2[1], terms2[2], terms2[4]},
-        {NULL}
+    LSUP_Triple *trp2[] = {
+        LSUP_triple_new (terms2[0], terms2[2], terms2[4]),
+        LSUP_triple_new (terms2[0], terms2[3], terms2[4]),
+        LSUP_triple_new (terms2[0], terms2[3], terms2[5]),
+        LSUP_triple_new (terms2[1], terms2[2], terms2[4]),
+        NULL
     };
 
     LSUP_Term *usr1_uri = LSUP_iriref_new ("#usr1", NULL);
@@ -42,12 +42,18 @@ test_desc_create()
     ASSERT (gr1, "Error creating graph!");
     EXPECT_PASS (LSUP_graph_add (gr1, trp1, NULL));
 
+    for (size_t i = 0; trp1[i]; i++)
+        free (trp1[i]);
+
     LSUP_Term *usr2_uri = LSUP_iriref_new ("#usr2", NULL);
     LSUP_Graph *gr2 = LSUP_graph_new (NULL, usr2_uri, NULL);
     LSUP_term_free (usr2_uri);
     ASSERT (gr2, "Error creating graph!");
     EXPECT_PASS (LSUP_graph_add (gr2, trp2, NULL));
 
+    for (size_t i = 0; trp2[i]; i++)
+        free (trp2[i]);
+
     LSUP_Graph *data[] = {gr1, gr2, NULL};
     LSR_Desc *rsrc;
     EXPECT_PASS (LSR_desc_new_multi (data, &rsrc));