--- /dev/null
+#include <stdlib.h>
+#include <relation.h>
+
+#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;
+ if ( primary->arity != key->arity ) {
+ return -1;
+ }
+ int i = 0;
+ for ( ; i < primary->arity; i++ ) {
+ if ( key->columns[i] && primary->columns[i] != key->columns[i] ) {
+ return -1;
+ }
+ }
+ i = (int) r->indexes.size;
+ vector_append( &r->indexes, hashvector_create( 1, (itemkeyfun*) key ) );
+ 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;
+}
+
+// Iteration context for adding or querying a relation
+typedef struct {
+ itemkeyfun *columns;
+ vector knockouts;
+ void *key;
+} knockoutdata;
+
+// Determine matches to ((knockoutdata*)data)->key in
+// (hashvector*)item, optionally using ((knockoutdata*)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 );
+ }
+ }
+ }
+ return 0;
+}
+
+// delete the (tuple*)item from the (hashvector*)data
+static int knockout_delete_item(vector_index index,void *item,void *data) {
+ hashvector_delete( (hashvector*)data, item );
+ return 0;
+}
+
+// delete the tuples of (vector*)data from the (hashvector*)item
+static int knockout_delete(vector_index index,void *item,void *data) {
+ vector_iterate( (vector*)data, 0, knockout_delete_item, item );
+ return 0;
+}
+
+// add the (tuple*)data to the (hashvector*)item
+static int knockout_add(vector_index index,void *item,void *data) {
+ hashvector_add( (hashvector*)item, 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 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 ) {
+ return 0;
+ }
+ return vector_clone( 3, &data.knockouts );
+}
+
--- /dev/null
+#ifndef relation_H
+#define relation_H
+
+#include <hashvector.h>
+#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.
+ */
+typedef struct {
+ vector indexes; // one or more indexes over the tuple collection
+ tupleschema *columns;
+} relation;
+
+/**
+ * Create a relation of the given arity and tupleschema.
+ */
+extern relation *relation_create(int arity,tupleschema *schema);
+
+/**
+ * Add a a key index to the relation by identifying the value part for
+ * this index.
+ */
+extern int relation_addindex(relation *r,tupleschema *ts);
+
+
+/**
+ * Add a tuple to a relation.
+ * \returns a vector of all knocked out tuples.
+ */
+extern vector *relation_add(relation *r,tuple *t);
+
+#endif