revamp relation implementation
authorRalph Ronnquist <ralph.ronnquist@gmail.com>
Wed, 6 Jul 2022 04:46:32 +0000 (14:46 +1000)
committerRalph Ronnquist <ralph.ronnquist@gmail.com>
Wed, 6 Jul 2022 04:46:32 +0000 (14:46 +1000)
vector/hashvector.c
vector/hashvector.h
vector/relation.c
vector/relation.h
vector/tupleitem.c
vector/tupleitem.h

index c9c2fb3ea7e360da75553ec3efb156010eefd100..4d89890e172b0874deafd6329c29d4625f18d936 100644 (file)
@@ -56,16 +56,27 @@ static void **hashvector_find_slot(
     }
 }
 
-// Find the keyed element, and assign the x pointer, or assign 0.
-// Returns 1 if element is found and 0 otherwise.
-int hashvector_find(hashvector *hv,void *key,void **x) {
-    unsigned long i;
-    void **p = hashvector_find_slot( hv, key, &i, 0 );
-    if ( p && *p && *p != HV_HOLE ) {
-       if ( x ) {
-           *x = *p;
+// Find the keyed element at or after the index. Update index and
+// return item.
+void *hashvector_next(hashvector *hv,vector_index *index,void *key) {
+    unsigned long i = index? *index : 0;
+    for ( ; i < hv->table.size; i++ ) {
+       void **p = hashvector_find_slot( hv, key, &i, 0 );
+       if ( p == 0 ) {
+           break;
        }
-       return 1;
+       if ( *p && *p != HV_HOLE ) {
+           if ( key && hv->type->haskey( hv->type, key, *p ) == 0 ) {
+               continue;
+           }
+           if ( index ) {
+               (*index) = i;
+           }
+           return *p;
+       }
+    }
+    if ( index ) {
+       (*index) = hv->table.size;
     }
     return 0;
 }
@@ -176,7 +187,7 @@ unsigned long hashvector_hashcode(unsigned char *key,unsigned long n) {
 }
 
 
-hashvector *hashvector_create(int variant,itemkeyfun *type) {
+hashvector *hashvector_create(enum vector_variant variant,itemkeyfun *type) {
     hashvector *hv = (hashvector*) malloc( sizeof( hashvector ) );
     (*hv) = (hashvector) {
        .table = (vector) {
index 1c3f387443bda77cd212479d3dc56a74c50532d5..a3810f9da2440ab738701306da842d0571793e5f 100644 (file)
@@ -71,13 +71,15 @@ typedef struct {
 #define HV_HOLE ((void*) 1)
 
 /**
- * Find the item, if any, with the given key and assign *x with its
- * address. Returns 1 on success and 0 on failure to find the keyed
- * item. Note that when the function returns 0, *x is unchanged.
+ * \brief Find next keyed item at or after the given index.
+ * \param hv is the hasvector concerned
+ * \param index is a pointer to the index to advance
+ * \param key is the query key
+ * \returns the next matching item, or 0 if none, with the index updated.
  *
  * \related hashvector
  */
-extern int hashvector_find(hashvector *hv,void *key,void **x);
+extern void *hashvector_next(hashvector *hv,vector_index *i,void *key);
 
 /**
  * Add the given item into the hashvector, growing the hashvector as
@@ -124,6 +126,7 @@ extern unsigned long hashvector_hashcode(unsigned char *key,unsigned long n);
 /**
  * Create a hashvector for of given variant for given itemkeyfun*
  */
-extern hashvector *hashvector_create(int variant,itemkeyfun *type);
+extern hashvector *hashvector_create(
+    enum vector_variant variant,itemkeyfun *type);
 
 #endif
index 330b3def071f70feec70438e4aa6fa95f212628d..8ab343f6cb739104768bd02116a5472f9ce8dfee 100644 (file)
@@ -1,16 +1,36 @@
 #include <stdlib.h>
 #include <relation.h>
 
+relation *relation_create(tupleschema *schema) {
+    relation *r = (relation *) malloc( sizeof( relation ) );
+    (*r) = (relation) {
+       .content = (hashvector) {
+           .table = (vector) {
+               .variant = nibble_index_levels,
+               .size = 16,
+               .entries = 0
+           },
+           .fill = 0, .holes = 0, .type = (itemkeyfun*) schema
+       },
+       .constraints = (vector) {
+           .variant = single_index_level,
+           .size = 0,
+           .entries = 0
+       },
+    };
+    return r;
+}
+
 #define COPYA(T,P,N) (T*) memcpy( malloc( N * sizeof(T) ), P, N * sizeof( T ) )
 #define COPY(T,P) COPYA(T,P,1)
 
 // Add an indexing hashvector to the relation using the nominated
 // column indexes being the value part. the key must be a clone of the
 // relation columns but with some columns reset.
-int relation_addindex(relation *r,tupleschema *key) {
-    tupleschema *primary = r->columns;
+int relation_add_contraint(relation *r,tupleschema *key) {
+    tupleschema *primary = (tupleschema *) r->content.type;
     if ( primary->arity != key->arity ) {
-       return -1;
+       return -1; // no good
     }
     int i = 0;
     for ( ; i < primary->arity; i++ ) {
@@ -18,44 +38,29 @@ int relation_addindex(relation *r,tupleschema *key) {
            return -1;
        }
     }
-    i = (int) r->indexes.size;
-    vector_append( &r->indexes, hashvector_create( 1, (itemkeyfun*) key ) );
+    i = (int) r->constraints.size;
+    vector_append(
+       &r->constraints,
+       hashvector_create( nibble_index_levels, &key->base ) );
     return i;
 }
 
-relation *relation_create(int arity,tupleschema *schema) {
-    relation *r = (relation *) malloc( sizeof( relation ) );
-    (*r) = (relation) {
-       .indexes = {
-       .variant = 2,
-       .size = 0,
-       .entries = 0
-       },
-       .columns = schema
-    };
-    return r;
-}
-
+//============== Adding an item =============
 // Iteration context for adding or querying a relation
 typedef struct {
-    itemkeyfun *columns;
+    relation *rel;
     vector knockouts;
-    void *key;
-} knockoutdata;
+    void *item;
+} knockout;
 
-// Determine matches to ((knockoutdata*)data)->key in
-// (hashvector*)item, optionally using ((knockoutdata*)data)->columns
+// Determine matches to ((knockout*)data)->key in
+// (hashvector*)item, optionally using ((knockout*)data)->columns
 // for ignoring full matches to the key tuple.
 static int knockout_check(vector_index index,void *item,void *data) {
-    knockoutdata *kod = (knockoutdata*) data;
-    void *old = 0;
-    if ( hashvector_find( (hashvector*) item, kod->key, &old ) ) {
-       if ( kod->columns == 0 ||
-            kod->columns->haskey( kod->columns, old, kod->key ) == 0 ) {
-           if ( vector_find( &kod->knockouts, old ) >= kod->knockouts.size ) {
-               vector_append( &kod->knockouts, old );
-           }
-       }
+    knockout *kod = (knockout*) data;
+    void *old = hashvector_next( (hashvector*) item, 0, kod->item );
+    if ( old ) {
+       vector_append( &kod->knockouts, old );
     }
     return 0;
 }
@@ -78,23 +83,58 @@ static int knockout_add(vector_index index,void *item,void *data) {
     return 0;
 }
 
-// add a tuple to a relation and return a vector of knocked out
-// tuples, if any, or 0 otherwise.
-vector *relation_add(relation *r,tuple *x) {
-    knockoutdata data = {
-       .columns = &(r->columns->functions),
-       .knockouts = { .variant = 2, .size = 0, .entries = 0 },
-       .key = x
+// Find and remove all collisions for a query, unless "add" is
+// non-zero in which case the function aborts if there is any match in
+// the main content.
+static int knockout_clear(knockout *this,relation *r,tuple *item,int add) {
+    (*this) = (knockout) {
+       .rel = r,
+       .knockouts = {
+           .variant = bitpair_index_levels,
+           .size = 0, .entries = 0
+       },
+       .item = item
     };
-    // Find all knockouts
-    vector_iterate( &r->indexes, 0, knockout_check, &data );
-    // Delete them
-    vector_iterate( &r->indexes, 0, knockout_delete, &data.knockouts );
-    // Add the new tuple
-    vector_iterate( &r->indexes, 0, knockout_add, x );
-    if ( data.knockouts.size == 0 ) {
+    knockout_check( 0, &r->content, this );
+    if ( add && this->knockouts.size > 0 ) {
        return 0;
     }
-    return vector_clone( 3, &data.knockouts );
+    // Find all constraint knockouts
+    vector_iterate( &r->constraints, 0, knockout_check, this );
+    if ( this->knockouts.size > 0 ) {
+       // Delete them from all tables
+       vector_iterate(
+           &this->knockouts, 0, knockout_delete_item, &r->content );
+       vector_iterate(
+           &r->constraints, 0, knockout_delete, &this->knockouts );
+    }
+    return 1;
+}
+
+// add a tuple to a relation and return a vector of knocked out
+// tuples, if any, or 0 otherwise.
+vector *relation_add(relation *r,tuple *item) {
+    knockout data;
+    if ( knockout_clear( &data, r, item, 1 ) ) {
+       // Add the new tuple
+       hashvector_add( &r->content, item );
+       vector_iterate( &r->constraints, 0, knockout_add, item );
+       if ( data.knockouts.size > 0 ) {
+           return vector_clone( single_index_level, &data.knockouts );
+       }
+    }
+    return 0;
+}
+
+vector *relation_delete(relation *r,tuple *item) {
+    knockout data;
+    (void) knockout_clear( &data, r, item, 0 );
+    if ( data.knockouts.size > 0 ) {
+       return vector_clone( single_index_level, &data.knockouts );
+    }
+    return 0;
 }
 
+void *relation_next(relation *r,vector_index *index,tuple *query) {
+    return hashvector_next( &r->content, index, query );
+}
index b0996c16b34510649b6235e836a73b496fc80c2d..c589176d3387601fae18fc29ad57ba954740ba26 100644 (file)
@@ -5,24 +5,32 @@
 #include <tupleitem.h>
 
 /**
- * A relation is an implementation of a tuple set with a primary key
- * through a hashvector whose "type" defines the key. Additional
- * hashvectors may be added to impose additional key restrictions and
- * the final relation is the intersection of them all.
- *
- * The relation record itself holds the column schema whereas the
- * indexes hold key schemas that in practice are copies of the column
- * schema with some columns blanked out.
+ * A relation is an implementation of a tuple set with (optinal) key
+ * constraints. The store is a hashvector whose "type" is a
+ * tupleschema that defines the columns. The key constraints are
+ * represented as additional hashvectors whose tupleschemas are clones
+ * of the column schema with some columns excluded.
  */
 typedef struct {
-    vector indexes; // one or more indexes over the tuple collection
-    tupleschema *columns;
+    /**
+     * This is the primary content store for the relation. Its type
+     * should be a tupleschema declaring the "item types" for the
+     * relation columns.
+     */
+    hashvector content;
+
+    /**
+     * This is a collection of relational constraints, if any, which
+     * are represented as hashvectors whose tupleschemas are clones of
+     * the content tupleschema with some columns excluded.
+     */
+    vector constraints;
 } relation;
 
 /**
- * Create a relation of the given arity and tupleschema.
+ * Create a relation for the given tupleschema.
  */
-extern relation *relation_create(int arity,tupleschema *schema);
+extern relation *relation_create(tupleschema *schema);
 
 /**
  * Add a a key index to the relation by identifying the value part for
@@ -32,9 +40,24 @@ extern int relation_addindex(relation *r,tupleschema *ts);
 
 
 /**
- * Add a tuple to a relation.
+ * \brief Add a tuple to a relation.
+ * \param r is th relation concerned.
+ * \param t is the tuple to add.
+ *
  * \returns a vector of all knocked out tuples.
  */
 extern vector *relation_add(relation *r,tuple *t);
 
+/**
+ * \brief Delete all tuples matching to the query.
+ * \returns all deleted tuples.
+ */
+extern vector *relation_delete(relation *r,tuple *query);
+
+/**
+ * \brief Return next tuple matching the query at or after the index.
+ * \returns any such matching tuple and updates *index to its index.
+ */
+extern void *relation_next(relation *r,vector_index *index,tuple *query);
+
 #endif
index 14c3b3ba89795aa1f928efd003353e864d7d8ce6..99539be33c1c7c80b4c45d19e27a7963d7afad98 100644 (file)
@@ -45,13 +45,19 @@ static int tupleitem_haskey(itemkeyfun *this,void *item,void *key) {
     tuple *kp = (tuple*) key;
     tuple *tp = (tuple*) item;
     int i = 0;
-    int haskey = 1;
     for ( ; i < def->arity; i++ ) {
-       if ( COLUMN[i] ) {
-           haskey &= COLUMN[i]->haskey( COLUMN[i], (*tp)[i], (*kp)[i] );
+       if ( COLUMN[i] == 0 ) {
+           if ( (*kp)[i] &&  (*tp)[i] != (*kp)[i] ) {
+               return 0;
+           }
+           continue;
+       }
+       if ( (*kp)[i] &&
+            COLUMN[i]->haskey( COLUMN[i], (*tp)[i], (*kp)[i] ) == 0 ) {
+           return 0;
        }
     }
-    return haskey;
+    return 1;
 }
 
 
@@ -110,7 +116,7 @@ tuple *tuple_create(int arity,...) {
 tupleschema *tupleschema_create(int arity,tuple *columns) {
     tupleschema *ts = (tupleschema*) malloc( sizeof( tupleschema ) );
     (*ts) = (tupleschema) {
-       .functions = {
+       .base = {
            .hashcode = tupleitem_hashcode,
            .haskey = tupleitem_haskey,
            .itemkey = tupleitem_itemkey,
@@ -153,3 +159,4 @@ unsigned long tuple_mask(int arity,tuple *t) {
     }
     return mask;
 }
+
index 595666848c34a9f49be0a0158c1385b1991b3979..889b875f29ab816a7795602654f9ca3c5fac3e1f 100644 (file)
@@ -22,7 +22,7 @@ typedef struct {
      * itemkeyfun pointer to be within a tupleschema record so as to
      * provide the handling of the tuple columns.
      */
-    itemkeyfun functions;
+    itemkeyfun base;
 
     /**
      * This is the number of columns in a tuple.
@@ -53,6 +53,11 @@ extern tupleschema *tupleschema_create(int arity,tuple *columns);
  */
 extern tupleschema *tupleschema_mask(tupleschema *schema,...);
 
+/**
+ * \brief Return 1/0 to indicate whether the query matches the item.
+ */
+extern int tupleschema_match(tupleschema *def,tuple *query,tuple *item);
+
 /**
  * The TUPLEITEMINIT macro is used for initializing a tupleschema
  * record appropriately for a given arity and corresponding sequence