major reorganisation
[rrq/rrqmisc.git] / logic / 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_clone( ts->columns );
34     int i = 0;
35     va_start( ap, r );
36     for ( ; i < columns->size; i++ ) {
37         if ( va_arg( ap, int ) == 0 ) {
38             columns->elements[i] = 0;
39         }
40     }
41     va_end( ap );
42     ts = TupleSchema_create( columns );
43     i = (int) r->constraints.size;
44     Vector_append(
45         &r->constraints,
46         HashVector_create( Nibble_index_levels, (ItemKeyFun*) ts ) );
47     return i;
48 }
49
50 //============== Adding an item =============
51 // Iteration context for adding or Querying a Relation
52 typedef struct {
53     Relation *rel;
54     HashVector knockouts;
55     void *item;
56 } Knockout;
57
58 // Determine matches to ((Knockout*)data)->key in
59 // (HashVector*)item, optionally using ((Knockout*)data)->columns
60 // for ignoring full matches to the key tuple.
61 static int knockout_check(VectorIndex index,void *item,void *data) {
62     Knockout *kod = (Knockout*) data;
63     void *key = kod->item;
64     HashVector *hv = (HashVector*) item;
65     TupleSchema *type = (TupleSchema *)hv->type;
66     VectorIndex i = 0;
67     for ( ; i < hv->table.size; i++ ) {
68         void *old = HashVector_next( hv, &i );
69         if ( old ) {
70             if ( key && type->base.haskey( type, old, key ) == 0 ) {
71                 continue;
72             }
73             HashVector_add( &kod->knockouts, old );
74         }
75     }
76     return 0;
77 }
78
79 // delete the (tuple*)item from the (HashVector*)data
80 static int knockout_delete(VectorIndex index,void *item,void *data) {
81     HashVector_delete( (HashVector*) item, data );
82     return 0;
83 }
84
85 // add the (tuple*)data to the (HashVector*)item
86 static int knockout_add(VectorIndex index,void *item,void *data) {
87     HashVector_add( (HashVector*)item, data );
88     return 0;
89 }
90
91 // Find and remove all collisions for a Query, unless "add" is
92 // non-zero in which case the function aborts if there is any match in
93 // the main content.
94 static int knockout_clear(Knockout *this,Relation *r,Tuple *item,int add) {
95     (*this) = (Knockout) {
96         .rel = r,
97         .knockouts = {
98             .table = {
99                 .variant = Nibble_index_levels, .size = 16, .entries = 0
100             },
101             .fill = 0, .holes = 0, .type = r->content.type,
102         },
103         .item = item
104     };
105     knockout_check( 0, &r->content, this );
106     if ( add ) {
107         if ( this->knockouts.fill > 0 ) {
108             return 0;
109         }
110         // Find all constraint knockouts for addition
111         Vector_iterate( &r->constraints, 0, knockout_check, this );
112     }
113     if ( this->knockouts.fill > 0 ) {
114         // Delete them from all tables
115         VectorIndex i;
116         for ( i = 0; i < this->knockouts.table.size; i++ ) {
117             void *t = HashVector_next( &this->knockouts, &i );
118             if ( t ) {
119                 HashVector_delete( &r->content, t );
120                 Vector_iterate( &r->constraints, 0, knockout_delete, t );
121             }
122         }
123     }
124     return 1;
125 }
126
127 // add a tuple to a Relation and return a Vector of knocked out
128 // tuples, if any, or 0 otherwise.
129 Vector *Relation_add(Relation *r,Tuple *item) {
130     Knockout data;
131     if ( knockout_clear( &data, r, item, 1 ) ) {
132         // Add the new tuple
133         HashVector_add( &r->content, item );
134         Vector_iterate( &r->constraints, 0, knockout_add, item );
135         return HashVector_contents( &data.knockouts, single_index_level, 0 );
136     }
137     return 0;
138 }
139
140 Vector *Relation_delete(Relation *r,Tuple *item) {
141     Knockout data;
142     (void) knockout_clear( &data, r, item, 0 );
143     return HashVector_contents( &data.knockouts, single_index_level, 0 );
144 }
145
146 void *Relation_next(Relation *r,VectorIndex *index,Tuple *query) {
147     HashVector *hv = &r->content;
148     void *key = query;
149     TupleSchema *type = (TupleSchema *) hv->type;
150     for ( ; (*index) < hv->table.size; (*index)++ ) {
151         void *old = HashVector_next( hv, index );
152         if ( old ) {
153             if ( key && type->base.haskey( type, old, key ) == 0 ) {
154                 continue;
155             }
156             return old;
157         }
158     }
159     (*index) = hv->table.size;
160     return 0;
161 }
162