Add "relation" implementation based on hashvector
[rrq/rrqmisc.git] / vector / relation.c
1 #include <stdlib.h>
2 #include <relation.h>
3
4 #define COPYA(T,P,N) (T*) memcpy( malloc( N * sizeof(T) ), P, N * sizeof( T ) )
5 #define COPY(T,P) COPYA(T,P,1)
6
7 // Add an indexing hashvector to the relation using the nominated
8 // column indexes being the value part. the key must be a clone of the
9 // relation columns but with some columns reset.
10 int relation_addindex(relation *r,tupleschema *key) {
11     tupleschema *primary = r->columns;
12     if ( primary->arity != key->arity ) {
13         return -1;
14     }
15     int i = 0;
16     for ( ; i < primary->arity; i++ ) {
17         if ( key->columns[i] && primary->columns[i] != key->columns[i] ) {
18             return -1;
19         }
20     }
21     i = (int) r->indexes.size;
22     vector_append( &r->indexes, hashvector_create( 1, (itemkeyfun*) key ) );
23     return i;
24 }
25
26 relation *relation_create(int arity,tupleschema *schema) {
27     relation *r = (relation *) malloc( sizeof( relation ) );
28     (*r) = (relation) {
29         .indexes = {
30         .variant = 2,
31         .size = 0,
32         .entries = 0
33         },
34         .columns = schema
35     };
36     return r;
37 }
38
39 // Iteration context for adding or querying a relation
40 typedef struct {
41     itemkeyfun *columns;
42     vector knockouts;
43     void *key;
44 } knockoutdata;
45
46 // Determine matches to ((knockoutdata*)data)->key in
47 // (hashvector*)item, optionally using ((knockoutdata*)data)->columns
48 // for ignoring full matches to the key tuple.
49 static int knockout_check(vector_index index,void *item,void *data) {
50     knockoutdata *kod = (knockoutdata*) data;
51     void *old = 0;
52     if ( hashvector_find( (hashvector*) item, kod->key, &old ) ) {
53         if ( kod->columns == 0 ||
54              kod->columns->haskey( kod->columns, old, kod->key ) == 0 ) {
55             if ( vector_find( &kod->knockouts, old ) >= kod->knockouts.size ) {
56                 vector_append( &kod->knockouts, old );
57             }
58         }
59     }
60     return 0;
61 }
62
63 // delete the (tuple*)item from the (hashvector*)data
64 static int knockout_delete_item(vector_index index,void *item,void *data) {
65     hashvector_delete( (hashvector*)data, item );
66     return 0;
67 }
68
69 // delete the tuples of (vector*)data from the (hashvector*)item
70 static int knockout_delete(vector_index index,void *item,void *data) {
71     vector_iterate( (vector*)data, 0, knockout_delete_item, item );
72     return 0;
73 }
74
75 // add the (tuple*)data to the (hashvector*)item
76 static int knockout_add(vector_index index,void *item,void *data) {
77     hashvector_add( (hashvector*)item, data );
78     return 0;
79 }
80
81 // add a tuple to a relation and return a vector of knocked out
82 // tuples, if any, or 0 otherwise.
83 vector *relation_add(relation *r,tuple *x) {
84     knockoutdata data = {
85         .columns = &(r->columns->functions),
86         .knockouts = { .variant = 2, .size = 0, .entries = 0 },
87         .key = x
88     };
89     // Find all knockouts
90     vector_iterate( &r->indexes, 0, knockout_check, &data );
91     // Delete them
92     vector_iterate( &r->indexes, 0, knockout_delete, &data.knockouts );
93     // Add the new tuple
94     vector_iterate( &r->indexes, 0, knockout_add, x );
95     if ( data.knockouts.size == 0 ) {
96         return 0;
97     }
98     return vector_clone( 3, &data.knockouts );
99 }
100