Cleanup Tuple to allow self-typing.
[rrq/rrqmisc.git] / vector / Relation.c
1 #include <stdarg.h>
2 #include <stdlib.h>
3 #include <Relation.h>
4
5 Relation *Relation_create(TupleSchema *schema) {
6     Relation *r = (Relation *) malloc( sizeof( Relation ) );
7     (*r) = (Relation) {
8         .content = (HashVector) {
9             .table = (Vector) {
10                 .variant = Nibble_index_levels,
11                 .size = 16,
12                 .entries = 0
13             },
14             .fill = 0, .holes = 0, .type = (ItemKeyFun*) schema
15         },
16         .constraints = (Vector) {
17             .variant = single_index_level,
18             .size = 0,
19             .entries = 0
20         },
21     };
22     return r;
23 }
24
25 #define COPYA(T,P,N) (T*) memcpy( malloc( N * sizeof(T) ), P, N * sizeof( T ) )
26 #define COPY(T,P) COPYA(T,P,1)
27
28 // Add an indexing HashVector to the Relation using the given column
29 // flags with 1 indicating key column and 0 indicating value column.
30 int Relation_add_constraint(Relation *r,...) {
31     va_list ap;
32     TupleSchema *ts = (TupleSchema *) r->content.type;
33     Tuple *columns = (Tuple*) malloc(
34         sizeof( Tuple ) + ts->arity * sizeof( void* ) );
35     int i = 0;
36     va_start( ap, r );
37     for ( ; i < ts->arity; i++ ) {
38         if ( va_arg( ap, int ) ) {
39             columns->elements[i] = ts->columns->elements[i];
40         }
41     }
42     va_end( ap );
43     ts = TupleSchema_create( ts->arity, columns );
44     i = (int) r->constraints.size;
45     Vector_append(
46         &r->constraints,
47         HashVector_create( Nibble_index_levels, (ItemKeyFun*) ts ) );
48     return i;
49 }
50
51 //============== Adding an item =============
52 // Iteration context for adding or Querying a Relation
53 typedef struct {
54     Relation *rel;
55     HashVector knockouts;
56     void *item;
57 } Knockout;
58
59 // Determine matches to ((Knockout*)data)->key in
60 // (HashVector*)item, optionally using ((Knockout*)data)->columns
61 // for ignoring full matches to the key tuple.
62 static int knockout_check(VectorIndex index,void *item,void *data) {
63     Knockout *kod = (Knockout*) data;
64     VectorIndex i = 0;
65     for ( ; i < ((HashVector*) item)->table.size; i++ ) {
66         void *old = HashVector_next( (HashVector*) item, &i, kod->item );
67         if ( old ) {
68             HashVector_add( &kod->knockouts, old );
69         }
70     }
71     return 0;
72 }
73
74 // delete the (tuple*)item from the (HashVector*)data
75 static int knockout_delete(VectorIndex index,void *item,void *data) {
76     HashVector_delete( (HashVector*) item, data );
77     return 0;
78 }
79
80 // add the (tuple*)data to the (HashVector*)item
81 static int knockout_add(VectorIndex index,void *item,void *data) {
82     HashVector_add( (HashVector*)item, data );
83     return 0;
84 }
85
86 // Find and remove all collisions for a Query, unless "add" is
87 // non-zero in which case the function aborts if there is any match in
88 // the main content.
89 static int knockout_clear(Knockout *this,Relation *r,Tuple *item,int add) {
90     (*this) = (Knockout) {
91         .rel = r,
92         .knockouts = {
93             .table = {
94                 .variant = Nibble_index_levels, .size = 16, .entries = 0
95             },
96             .fill = 0, .holes = 0, .type = r->content.type,
97         },
98         .item = item
99     };
100     knockout_check( 0, &r->content, this );
101     if ( add ) {
102         if ( this->knockouts.fill > 0 ) {
103             return 0;
104         }
105         // Find all constraint knockouts for addition
106         Vector_iterate( &r->constraints, 0, knockout_check, this );
107     }
108     if ( this->knockouts.fill > 0 ) {
109         // Delete them from all tables
110         VectorIndex i;
111         for ( i = 0; i < this->knockouts.table.size; i++ ) {
112             void *t = HashVector_next( &this->knockouts, &i, 0 );
113             if ( t ) {
114                 HashVector_delete( &r->content, t );
115                 Vector_iterate( &r->constraints, 0, knockout_delete, t );
116             }
117         }
118     }
119     return 1;
120 }
121
122 // add a tuple to a Relation and return a Vector of knocked out
123 // tuples, if any, or 0 otherwise.
124 Vector *Relation_add(Relation *r,Tuple *item) {
125     Knockout data;
126     if ( knockout_clear( &data, r, item, 1 ) ) {
127         // Add the new tuple
128         HashVector_add( &r->content, item );
129         Vector_iterate( &r->constraints, 0, knockout_add, item );
130         return HashVector_contents( &data.knockouts, single_index_level, 0 );
131     }
132     return 0;
133 }
134
135 Vector *Relation_delete(Relation *r,Tuple *item) {
136     Knockout data;
137     (void) knockout_clear( &data, r, item, 0 );
138     return HashVector_contents( &data.knockouts, single_index_level, 0 );
139 }
140
141 void *Relation_next(Relation *r,VectorIndex *index,Tuple *query) {
142     return HashVector_next( &r->content, index, query );
143 }
144