From: Ralph Ronnquist Date: Wed, 6 Jul 2022 04:46:32 +0000 (+1000) Subject: revamp relation implementation X-Git-Url: https://git.rrq.au/?a=commitdiff_plain;h=d9858ce46cb09b9127c7246f628e65982aa43a15;p=rrq%2Frrqmisc.git revamp relation implementation --- diff --git a/vector/hashvector.c b/vector/hashvector.c index c9c2fb3..4d89890 100644 --- a/vector/hashvector.c +++ b/vector/hashvector.c @@ -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) { diff --git a/vector/hashvector.h b/vector/hashvector.h index 1c3f387..a3810f9 100644 --- a/vector/hashvector.h +++ b/vector/hashvector.h @@ -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 diff --git a/vector/relation.c b/vector/relation.c index 330b3de..8ab343f 100644 --- a/vector/relation.c +++ b/vector/relation.c @@ -1,16 +1,36 @@ #include #include +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 ); +} diff --git a/vector/relation.h b/vector/relation.h index b0996c1..c589176 100644 --- a/vector/relation.h +++ b/vector/relation.h @@ -5,24 +5,32 @@ #include /** - * 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 diff --git a/vector/tupleitem.c b/vector/tupleitem.c index 14c3b3b..99539be 100644 --- a/vector/tupleitem.c +++ b/vector/tupleitem.c @@ -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; } + diff --git a/vector/tupleitem.h b/vector/tupleitem.h index 5956668..889b875 100644 --- a/vector/tupleitem.h +++ b/vector/tupleitem.h @@ -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