--- /dev/null
+#include <stdarg.h>
+#include <stdlib.h>
+#include <AndQuery.h>
+
+static void AndQuery_reclaim(Query *this) {
+ AndQuery *q = (AndQuery*) this;
+ int i;
+ for ( i = 0; i < q->size; i++ ) {
+ Query_reclaim( q->conjuncts[i] );
+ }
+ free( q->conjuncts );
+ free( this );
+}
+
+static int AndQuery_next(
+ Query *this,BindingTable *bt,enum NextState state)
+{
+ AndQuery *q = (AndQuery*) this;
+ int i = q->size - 1;
+ enum NextState s = subsequent;
+ switch ( state ) {
+ case initial:
+ q->active = 1;
+ i = 0;
+ s = initial;
+ // Fall through?
+ case subsequent:
+ while ( i < q->size ) {
+ if ( Query_next( q->conjuncts[i], bt, s ) ) {
+ continue;
+ }
+ Query_next( q->conjuncts[i], bt, restore );
+ if ( i-- == 0 ) {
+ // The first conjunct now exhausted
+ q->active = 0;
+ return 0;
+ }
+ s = subsequent;
+ }
+ return 1;
+ case restore:
+ if ( q->active ) {
+ for ( ; i >= 0; i-- ) {
+ Query_next( q->conjuncts[i], bt, restore );
+ }
+ }
+ q->active = 0;
+ return 0;
+ }
+ return 0;
+}
+
+static void AndQuery_variables(Query *this,HashVector *hv) {
+ AndQuery *q = (AndQuery*) this;
+ int i;
+ for ( i = 0; i < q->size; i++ ) {
+ Query_variables( q->conjuncts[i], hv );
+ }
+}
+
+static struct QueryCallbacks AndQuery_def = {
+ .reclaim = AndQuery_reclaim,
+ .next = AndQuery_next,
+ .variables = AndQuery_variables
+};
+
+Query *Query_and(int n,...) {
+ va_list args;
+ AndQuery *q = (AndQuery *)
+ malloc( sizeof( AndQuery ) );
+ (*q) = (AndQuery) {
+ .def = &AndQuery_def,
+ .active = 0,
+ .size = n,
+ .conjuncts = (Query**) malloc( n * sizeof( Query* ) )
+ };
+ int i;
+ va_start( args, n );
+ for ( i = 0; i < n; i++ ) {
+ q->conjuncts[i] = va_arg( args, Query* );
+ }
+ va_end( args );
+ return (Query*) q;
+}
+
--- /dev/null
+#ifndef AndQuery_H
+#define AndQuery_H
+
+#include <Query.h>
+
+/**
+ * AndQuery represents a conjunction of sub queries.
+ * \extends Query
+ * \related Query
+ */
+typedef struct {
+ struct QueryCallbacks *def;
+ int active;
+ int size;
+ Query **conjuncts;
+} AndQuery;
+
+
+#endif
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <AssignQuery.h>
+
+// Release any memory.
+static void AssignQuery_reclaim(Query *this) {
+ AssignQuery *q = (AssignQuery*) this;
+ free( q->saved );
+ free( this );
+}
+
+static int AssignQuery_check(Tuple *a,Tuple *b) {
+ int i;
+ for ( i = 0; i < a->size; i++ ) {
+ char *value = a->elements[i];
+ char *current = b->elements[i];
+ if ( value && current && current != value &&
+ strcmp( current, value ) != 0 ) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+// Make names have given values and return 1. If any name has a
+// different value then return 0. Values are strings.
+static int AssignQuery_next(
+ Query *this,BindingTable *bt,enum NextState state) {
+ AssignQuery *q = (AssignQuery*) this;
+ switch ( state ) {
+ case initial:
+ if ( q->saved == 0 ) {
+ q->saved = Tuple_clone( q->names );
+ } else {
+ memcpy( q->saved, q->names, q->names->size * sizeof( void* ) );
+ }
+ BindingTable_deref( bt, q->saved );
+ // Check with new values
+ if ( AssignQuery_check( q->values, q->saved ) ) {
+ BindingTable_set_all( bt, q->names, q->values, 0 );
+ return 1;
+ }
+ // Fall through
+ case subsequent:
+ case restore:
+ if ( q->saved ) {
+ BindingTable_set_all( bt, q->names, q->saved, 1 );
+ free( q->saved );
+ q->saved = 0;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+static void AssignQuery_variables(Query *this,HashVector *hv) {
+ AssignQuery *q = (AssignQuery*) this;
+ unsigned long i;
+ for ( i = 0; i < q->names->size; i++ ) {
+ HashVector_add( hv, q->names->elements[i] );
+ }
+}
+
+static struct QueryCallbacks AssignQuery_def = {
+ .reclaim = AssignQuery_reclaim,
+ .next = AssignQuery_next,
+ .variables = AssignQuery_variables
+};
+
+/**
+ * Return a Query object representing an assignment of one or more
+ * variables.
+ */
+Query *Query_assign(int arity,Tuple *names,Tuple *values) {
+ AssignQuery *q = (AssignQuery*)
+ malloc( sizeof( AssignQuery ) );
+ (*q) = (AssignQuery) {
+ .def = &AssignQuery_def,
+ .names = names,
+ .values = values,
+ .saved = 0
+ };
+ return (Query*) q;
+}
--- /dev/null
+#ifndef AssignQuery_H
+#define AssignQuery_H
+
+#include <QueryCallbacks.h>
+#include <Tuple.h>
+
+/**
+ * AssignQuery represents an assignment of values to names.
+ *
+ * \extends Query
+ * \related Query
+ */
+typedef struct {
+ struct QueryCallbacks *def;
+ Tuple *names;
+ Tuple *values;
+ Tuple *saved;
+} AssignQuery;
+
+void AssignQuery_assign(
+ BindingTable *bt,Tuple *names,Tuple *values,int all);
+
+#endif
--- /dev/null
+#include <string.h>
+#include <HashVector.h>
+#include <Binding.h>
+
+/**
+ * This callback function returns the hashcode of a Binding key, which
+ * is its name string.
+ */
+static unsigned long Binding_hashcode(void *this,void *key) {
+ return HashVector_hashcode(
+ (unsigned char *) key, strlen( (char*) key ) );
+}
+
+/**
+ * This callback function determines whether a Binding item has a
+ * given key or not.
+ */
+static int Binding_haskey(void *this,void *item,void *key) {
+ Binding *b = (Binding*) item;
+ char *name = (char *) key;
+ return strcmp( name, (char*) b->name ) == 0;
+}
+
+/**
+ * This callback function returns the key of a Binding itme, which is
+ * its name string.
+ */
+static void *Binding_itemkey(void *this,void *item) {
+ return ((Binding*) item)->name;
+}
+
+
+/**
+ * This callback function handles the "release" of a Binding key,
+ * which is to do nothing, since the name string memory is not managed
+ * here.
+ */
+static void Binding_releasekey(void *this,void *key) {
+}
+
+/**
+ * This callback function writes a representation of a Binding item
+ * into a character buffer.
+ */
+static int Binding_tostring(void *this,void *item,char *buffer,int limit) {
+ Binding *b = (Binding*) item;
+ return snprintf( buffer, limit, "{%s,%p}", b->name, b->value );
+}
+
+/**
+ * This is the "item type" for Binding items.
+ */
+ItemKeyFun Bindingitem = {
+ .hashcode = Binding_hashcode,
+ .haskey = Binding_haskey,
+ .itemkey = Binding_itemkey,
+ .releasekey = Binding_releasekey,
+ .tostring = Binding_tostring
+};
+
--- /dev/null
+#ifndef Binding_H
+#define Binding_H
+
+#include <ItemKeyFun.h>
+
+/**
+ * \brief A Binding is an association of a (var*) name with a (void*)
+ * value.
+ *
+ * A \b (void*)0 value marks an "unbound" value.
+ */
+typedef struct Binding {
+ char *name;
+ void *value;
+} Binding;
+
+/**
+ * \brief This is the applicable ItemKeyFun callbacks for a Binding
+ * item.
+ */
+extern ItemKeyFun Bindingitem;
+
+#endif
#include <string.h>
#include <stdlib.h>
-/**
- * A Binding is an association between a name (char*) and a value
- * (void*).
- */
-typedef struct {
- char *name;
- void *value;
-} Binding;
-
-/**
- * This callback function should return the hashcode of a key. The
- * hashcode is used for indexing into the backing Vector for
- * finding the an item via its key. The same key must map
- * consistently to the same hashcode while the hashtable contains
- * an item with that key. Different keys map map to the same
- * hashcode, in which case the Vector placement is made at the
- * first empty or hole slot following the hashcode index.
- */
-static unsigned long binding_hashcode(void *this,void *key) {
- char *name = (char *) key;
- unsigned long n = strlen( name );
- return HashVector_hashcode( (unsigned char *) name, n );
-}
-
-/**
- * This callback function should determine whether an item has a
- * given key or not.
- */
-static int binding_haskey(void *this,void *item,void *key) {
- Binding *b = (Binding*) item;
- char *name = (char *) key;
- return strcmp( name, (char*) b->name ) == 0;
-}
-
-/**
- * This callback function should return a (possibly temporary) key
- * of an item. It can be anything (i.e., with or without internal
- * structure) as it is treated as an identifier that other
- * callbacks recognize. It is merely understood as a value in an
- * identity domain.
- */
-static void *binding_itemkey(void *this,void *item) {
- Binding *b = (Binding*) item;
- return b->name;
-}
-
-
-/**
- * This callback function should handle a key obtained from the
- * itemkey callback function, e.g., reclaim temporary allocation.
- */
-static void binding_releasekey(void *this,void *key) {
-}
-
-/**
- * This callback function writes a representation of an item into
- * a character buffer.
- */
-static int binding_tostring(void *this,void *item,char *buffer,int limit) {
- Binding *b = (Binding*) item;
- return snprintf( buffer, limit, "{%s,%p}", b->name, b->value );
-}
-
-/**
- * This is the "item type" for Binding items.
- */
-ItemKeyFun bindingitem = {
- .hashcode = binding_hashcode,
- .haskey = binding_haskey,
- .itemkey = binding_itemkey,
- .releasekey = binding_releasekey,
- .tostring = binding_tostring
-};
-
-BindingTable *BindingTable_create(BindingTable *next) {
+BindingTable *BindingTable_create() {
BindingTable *this = (BindingTable*) malloc( sizeof( BindingTable ) );
- this->table = (HashVector) {
+ (*this) = (HashVector) {
.table = (Vector) {
.variant = Nibble_index_levels, .size = 16, .entries = 0
- }, .fill = 0, .holes = 0, .type = &bindingitem
+ }, .fill = 0, .holes = 0, .type = &Bindingitem
};
- this->next = next;
return this;
}
-BindingTable *BindingTable_release(BindingTable *bt) {
+void BindingTable_release(BindingTable *bt) {
if ( bt ) {
- BindingTable *next = bt->next;
- Vector_resize( &bt->table.table, 0, Vector_free_any, 0 );
+ Vector_resize( &bt->table, 0, Vector_free_any, 0 );
free( bt );
- return next;
}
- return 0;
}
void BindingTable_set(BindingTable *bt,char *name,void *value) {
- Binding *b = (Binding*) HashVector_find( &bt->table, name );
+ Binding *b = (Binding*) HashVector_find( bt, name );
if ( b == 0 ) {
b = (Binding*) malloc( sizeof( Binding ) );
b->name = name;
- HashVector_add( &bt->table, b );
+ HashVector_add( bt, b );
}
b->value = value;
}
void *BindingTable_get(BindingTable *bt,char *name) {
- for ( ; bt; bt = bt->next ) {
- Binding *b = (Binding*) HashVector_find( &bt->table, name );
- if ( b ) {
- return b->value;
- }
- }
- return 0;
+ Binding *b = (Binding*) HashVector_find( bt, name );
+ return b? b->value : 0;
}
-void BindingTable_deref(BindingTable *bt,int arity,Tuple *t) {
- int i;
- for ( i = 0; i < arity; i++ ) {
+void BindingTable_deref(BindingTable *bt,Tuple *t) {
+ unsigned long i;
+ for ( i = 0; i < t->size; i++ ) {
if ( t->elements[i] ) {
t->elements[i] = BindingTable_get( bt, t->elements[i] );
}
}
}
+#if 0
int BindingTable_unify(
BindingTable *bt,char *n1,char *n2,int (*eq)(void*,void*)) {
void *v1 = BindingTable_get( bt, n1 );
}
return ( v1 && v2 )? ( eq? ( eq( v1, v2 ) == 0 ) : ( v1 == v2 ) ) : 1;
}
+#endif
-#define COPYA(T,P,N) (T*) memcpy( malloc( N * sizeof(T) ), P, N * sizeof( T ) )
-
-Tuple *BindingTable_tuple_get(BindingTable *bt,int arity,Tuple *t) {
- Tuple *vt = Tuple_clone( arity, t );
- BindingTable_deref( bt, arity, vt );
+Tuple *BindingTable_get_all(BindingTable *bt,Tuple *t) {
+ Tuple *vt = Tuple_clone( t );
+ BindingTable_deref( bt, vt );
return vt;
}
-void BindingTable_tuple_set(BindingTable *bt,int arity,Tuple *nm,Tuple *vs) {
+void BindingTable_set_all(BindingTable *bt,Tuple *nm,Tuple *vs,int all) {
int i;
- for ( i = 0; i < arity; i++ ) {
+ for ( i = 0; i < nm->size; i++ ) {
BindingTable_set( bt, nm->elements[i], vs->elements[i] );
}
}
#include <HashVector.h>
#include <TupleSchema.h>
+#include <Binding.h>
/**
* A BindingTable is a chain of \ref HashVector "HashVectors" of
* Binding items that associate a (char*) name with a (void*) value.
*/
-typedef struct _BindingTable {
- HashVector table;
- struct _BindingTable *next;
-} BindingTable;
+typedef HashVector/*<Binding>*/ BindingTable;
/**
- * \brief Allocate a new \ref BindingTable and chain it to the one given.
+ * \brief Allocate a new \ref BindingTable.
*
* \returns the allocated \ref bandingtable.
*
* \related BindingTable
*/
-extern BindingTable *BindingTable_create(BindingTable *next);
+extern BindingTable *BindingTable_create();
/**
- * \brief Reclaim a \ref BindingTable with all its bindings and return
- * its chained.
+ * \brief Reclaim a \ref BindingTable with all its bindings.
*
* \param bt is the \ref BindingTable to reclaim.
*
- * \returns the chained \ref BindingTable.
- *
* \related BindingTable
*/
-extern BindingTable *BindingTable_release(BindingTable *bt);
+extern void BindingTable_release(BindingTable *bt);
/**
* \brief Set a Binding in a \ref BindingTable.
* \note Binding names are equal or not by means of strcmp, and each
* name has a at most single Binding.
*
+ * The name and the value are held as given with all memory management
+ * the callers responsibility. This function will however create new
+ * Binding objects and reclaim the old ones as needed.
+ *
* A value of \b 0 indicates "unbound".
*
* \related BindingTable
*
* \param name is the Binding variable name.
*
- * \param value is the Binding value.
+ * \returns the value of the found Binding, or \b 0 if none is found.
*
* \note Binding names are equal or not by means of strcmp, and each
* name has a at most single Binding.
*
- * This function scan's the \ref BindingTable chain for the first
- * assignment, if any, of the name. Note that a name can be made
- * unbound on top of being bound by setting it to \b 0.
+ * \note Not also that a name can be made unbound on top of being
+ * bound by setting it to \b 0.
*
* \related BindingTable
*/
*
* \param bt is the BindingTable concerned.
*
- * \param arity is the tuple arity.
- *
* \param t is the tuple of (char*) names to dereference.
*/
-void BindingTable_deref(BindingTable *bt,int arity,Tuple *t);
+extern void BindingTable_deref(BindingTable *bt,Tuple *t);
/**
- * \brief Unify two named bindings with option equal-value callback.
+ * \brief Set values for names, optionally unbinding names as well.
*
- * \param bt is the first BindingTable concerned.
- *
- * \param n1 is the first Binding name.
+ * \param bt is the bindingtable concerned.
*
- * \param n2 is the second Binding name.
+ * \param nm is the Tuple of names to bind.
*
- * \param eq is the optional equal-value callback that returns 0 if
- * the given values are equal.
+ * \param vs is the Tuple of values.
*
- * This function updates the top \ref BindingTable by assigning n1 or
- * n2 to any value the other has (in the chain) unless they have
- * different values (in the chain). If both are unassigned, then
- * neither get reassigned in the to both BindingTable/
+ * \param all is a flag to assign all (1) or only non-zero (0) values.
*
- * \related BindingTable
+ * \note The values tuple must be as wide as the names tuple.
*/
-extern int BindingTable_unify(
- BindingTable *bt,char *n1,char *n2,int (*eq)(void*,void*));
+extern void BindingTable_set_all(BindingTable *bt,Tuple *nm,Tuple *vs,int all);
#endif
LIBRARY = libvector.a
LIBOBJS = Vector.o HashVector.o
-LIBOBJS += Tuple.o TupleSchema.o integeritem.o stringitem.o
-LIBOBJS += Relation.o
-LIBOBJS += BindingTable.o Query.o
+LIBOBJS += Tuple.o TupleSchema.o integeritem.o stringitem.o Binding.o
+LIBOBJS += BindingTable.o Relation.o Query.o
+LIBOBJS += AssignQuery.o RelationQuery.o
+LIBOBJS += NotQuery.o AndQuery.o OrQuery.o
+#LIBOBJS += View.o
default: $(LIBRARY)
CFLAGS = -Wall -g -fmax-errors=1 -I.
LDLIBS = -lm
+%.h:
+ echo "#ifndef ${@:.h=_H}" > $@
+ echo "#define ${@:.h=_H}" >> $@
+ echo "#endif" >> $@
+
define STDCC
.INTERMEDIATE: $1.o
CLEANRM += $1.o
--- /dev/null
+#include <stdlib.h>
+#include <NotQuery.h>
+
+static void NotQuery_reclaim(Query *this) {
+ NotQuery *q = (NotQuery*) this;
+ Query_reclaim( q->query );
+ free( this );
+}
+
+static int NotQuery_next(
+ Query *this,BindingTable *bt,enum NextState state)
+{
+ NotQuery *q = (NotQuery*) this;
+ if ( state == initial ) {
+ if ( Query_next( q->query, bt, initial ) ) {
+ Query_next( q->query, bt, restore );
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static void NotQuery_variables(Query *this,HashVector *hv) {
+ Query_variables( ((NotQuery*) this)->query, hv );
+}
+
+static struct QueryCallbacks NotQuery_def = {
+ .reclaim = NotQuery_reclaim,
+ .next = NotQuery_next,
+ .variables = NotQuery_variables
+};
+
+Query *Query_not(Query *q) {
+ NotQuery *nq = (NotQuery*) malloc( sizeof( NotQuery ) );
+ (*nq) = (NotQuery) {
+ .def = &NotQuery_def,
+ .query = q
+ };
+ return (Query*) nq;
+}
--- /dev/null
+#ifndef NotQuery_H
+#define NotQuery_H
+
+#include <Query.h>
+
+/**
+ * NotQuery represents logical negation.
+ *
+ * \extends Query
+ */
+typedef struct NotQuery {
+ struct QueryCallbacks *def;
+ Query *query;
+} NotQuery;
+
+#endif
--- /dev/null
+#include <stdarg.h>
+#include <stdlib.h>
+#include <OrQuery.h>
+
+static void OrQuery_reclaim(Query *this) {
+ OrQuery *q = (OrQuery*) this;
+ int i;
+ for ( i = 0; i < q->size; i++ ) {
+ Query_reclaim( q->disjuncts[i] );
+ }
+ free( q->disjuncts );
+ free( this );
+}
+
+static int OrQuery_next( Query *this,BindingTable *bt,enum NextState state) {
+ OrQuery *q = (OrQuery*) this;
+ int i = q->index;
+ enum NextState s = subsequent;
+ switch ( state ) {
+ case initial:
+ i = 0;
+ s = initial;
+ case subsequent:
+ for ( ; i < q->size; i++ ) {
+ if ( Query_next( q->disjuncts[i], bt, s ) ) {
+ q->index = i;
+ return 1;
+ }
+ Query_next( q->disjuncts[i], bt, restore );
+ s = initial;
+ }
+ q->index = -1;
+ return 0;
+ case restore:
+ if ( i >= 0 ) {
+ Query_next( q->disjuncts[i], bt, restore );
+ q->index = -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+static void OrQuery_variables(Query *this,HashVector *hv) {
+ OrQuery *q = (OrQuery*) this;
+ int i;
+ for ( i = 0; i < q->size; i++ ) {
+ Query_variables( q->disjuncts[i], hv );
+ }
+}
+
+static struct QueryCallbacks OrQuery_def = {
+ .reclaim = OrQuery_reclaim,
+ .next = OrQuery_next,
+ .variables = OrQuery_variables
+};
+
+Query *Query_or(int n,...) {
+ va_list args;
+ OrQuery *q = (OrQuery *)
+ malloc( sizeof( OrQuery ) );
+ (*q) = (OrQuery) {
+ .def = &OrQuery_def,
+ .index = -1,
+ .size = n,
+ .disjuncts = (Query**) malloc( n * sizeof( Query* ) ),
+ };
+ int i;
+ va_start( args, n );
+ for ( i = 0; i < n; i++ ) {
+ q->disjuncts[i] = va_arg( args, Query* );
+ }
+ va_end( args );
+ return (Query*) q;
+}
+
--- /dev/null
+#ifndef OrQuery_H
+#define OrQuery_H
+
+#include <Query.h>
+
+/**
+ * OrQuery represents a disjunction of sub queries.
+ *
+ * \extends Query
+ */
+typedef struct {
+ struct QueryCallbacks *def;
+ int index;
+ int size;
+ Query **disjuncts;
+} OrQuery;
+
+#endif
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
#include <Query.h>
-#include <QueryCallbacks.h>
-
-/* ==================== AssignmentQuery ==================== */
/**
- * AssignmentQuery represents an assignment of values to names.
- * \extends Query
- * \related Query
+ * \brief Trampoline for the callback function to reclaim the Query
+ * memory for a given Query.
+ *
+ * \param this is the specific \ref Query concerned.
+ *
+ * Ground queries recalim their own state memory. Composite
+ * queries first propagate the reclaim call to its components, and
+ * thereafter reclaim their local state memory.
*/
-typedef struct {
- struct QueryCallbacks *def;
- int arity;
- Tuple *names;
- Tuple *values;
- Tuple *saved;
-} AssignmentQuery;
-
-// Release any memory.
-static void AssignmentQuery_reclaim(Query *this) {
- AssignmentQuery *q = (AssignmentQuery*) this;
- free( q->saved );
- free( this );
-}
-
-static int AssignmentQuery_check(int arity,Tuple *a,Tuple *b) {
- int i;
- for ( i = 0; i < arity; i++ ) {
- char *value = a->elements[i];
- char *current = b->elements[i];
- if ( value && current && current != value &&
- strcmp( current, value ) != 0 ) {
- return 0;
- }
- }
- return 1;
-}
-
-static void AssignmentQuery_assign(
- BindingTable *bt,int n,Tuple *names,Tuple *values,int all)
-{
- int i;
- for ( i = 0; i < n; i++ ) {
- if ( all || values->elements[i] ) {
- BindingTable_set( bt, names->elements[i], values->elements[i] );
- }
- }
-}
-
-// Make names have given values and return 1. If any name has a
-// different value then return 0. Values are strings.
-static int AssignmentQuery_next(
- Query *this,BindingTable *bt,enum NextState state) {
- AssignmentQuery *q = (AssignmentQuery*) this;
- switch ( state ) {
- case initial:
- if ( q->saved == 0 ) {
- q->saved = Tuple_clone( q->arity, q->names );
- } else {
- memcpy( q->saved, q->names, q->arity * sizeof( void* ) );
- }
- BindingTable_deref( bt, q->arity, q->saved );
- // Check with new values
- if ( AssignmentQuery_check( q->arity, q->values, q->saved ) ) {
- AssignmentQuery_assign( bt, q->arity, q->names, q->values, 0 );
- return 1;
- }
- // Fall through
- case subsequent:
- case restore:
- if ( q->saved ) {
- AssignmentQuery_assign( bt, q->arity, q->names, q->saved, 1 );
- free( q->saved );
- q->saved = 0;
- }
- return 0;
- }
- return 0;
+void Query_reclaim(Query *this) {
+ this->def->reclaim( this );
}
-static struct QueryCallbacks AssignmentQuery_def = {
- .reclaim = AssignmentQuery_reclaim,
- .next = AssignmentQuery_next
-};
-
/**
- * Return a Query object representing an assignment of one or more
- * variables.
+ * \brief Trampoline for the callback function to update the Binding
+ * table with a succession of alternative bindings.
+ *
+ * \param this is the specific \ref Query concerned.
+ *
+ * \param bt is the Binding table to set bindings in.
+ *
+ * \param s is the call "sub-command" for the function.
+ *
+ * \returns 1 if a new Binding is provided and 0 otherwise.
+ *
+ * This function is called repeatedly for successively obtaining
+ * the alternative bindings that satisfy the Query. The "initial"
+ * state sub-command tells the function to capture the incoming
+ * BindingTable state so that the function can later restore it
+ * upon the "restore" sub-command. Upon the "initial" command, the
+ * function also sets up the Binding table with its first Binding
+ * alternative. This is followed by a series of "subsequent"
+ * sub-command calls for the function to change the BindingTable
+ * for its succession of Binding alternatives. The function should
+ * return 1 after any successful Binding setup, and return 0 when
+ * it cannot setup any (more) Binding.
*/
-Query *Query_assign(int arity,Tuple *names,Tuple *values) {
- AssignmentQuery *q = (AssignmentQuery*)
- malloc( sizeof( AssignmentQuery ) );
- (*q) = (AssignmentQuery) {
- .def = &AssignmentQuery_def,
- .arity = arity,
- .names = names,
- .values = values,
- .saved = 0
- };
- return (Query*) q;
+int Query_next(Query *this,BindingTable *bt,enum NextState state) {
+ return this->def->next( this, bt, state );
}
-/* ==================== conjunction ==================== */
-
/**
- * ConjunctionQuery represents a conjunction of sub queries.
- * \extends Query
- * \related Query
+ * \brief Trampoline for the callback function that adds its binding
+ * names to the hashvector.
*/
-typedef struct {
- struct QueryCallbacks *def;
- int active;
- int size;
- Query **conjuncts;
-} ConjunctionQuery;
-
-static void ConjunctionQuery_reclaim(Query *this) {
- ConjunctionQuery *q = (ConjunctionQuery*) this;
- int i;
- for ( i = 0; i < q->size; i++ ) {
- q->conjuncts[i]->def->reclaim( q->conjuncts[i] );
- }
- free( q->conjuncts );
- free( this );
+void Query_variables(Query *this,HashVector *hv) {
+ this->def->variables( this, hv );
}
-static int ConjunctionQuery_next(
- Query *this,BindingTable *bt,enum NextState state)
+void Query_eval(
+ Query *q,BindingTable *bt,
+ int (*consequent)(BindingTable *bt,void *data),
+ void *data )
{
- ConjunctionQuery *q = (ConjunctionQuery*) this;
- int i = q->size - 1;
- enum NextState s = subsequent;
- switch ( state ) {
- case initial:
- q->active = 1;
- i = 0;
- s = initial;
- // Fall through?
- case subsequent:
- while ( i < q->size ) {
- if ( q->conjuncts[i]->def->next( q->conjuncts[i], bt, s ) ) {
- continue;
- }
- q->conjuncts[i]->def->next( q->conjuncts[i], bt, restore );
- if ( i-- == 0 ) {
- // The first conjunct now exhausted
- q->active = 0;
- return 0;
- }
- s = subsequent;
- }
- return 1;
- case restore:
- if ( q->active ) {
- for ( ; i >= 0; i-- ) {
- q->conjuncts[i]->def->next( q->conjuncts[i], bt, restore );
- }
- }
- q->active = 0;
- return 0;
- }
- return 0;
-}
-
-static struct QueryCallbacks ConjunctionQuery_def = {
- .reclaim = ConjunctionQuery_reclaim,
- .next = ConjunctionQuery_next
-};
-
-Query *Query_and(int n,...) {
- va_list args;
- ConjunctionQuery *q = (ConjunctionQuery *)
- malloc( sizeof( ConjunctionQuery ) );
- (*q) = (ConjunctionQuery) {
- .def = &ConjunctionQuery_def,
- .active = 0,
- .size = n,
- .conjuncts = (Query**) malloc( n * sizeof( Query* ) )
- };
- int i;
- va_start( args, n );
- for ( i = 0; i < n; i++ ) {
- q->conjuncts[i] = va_arg( args, Query* );
- }
- va_end( args );
- return (Query*) q;
-}
-
-/* ==================== disjunction ==================== */
-
-/**
- * DisjunctionQuery represents a disjunction of sub queries.
- * \extends Query
- * \related Query
- */
-typedef struct {
- struct QueryCallbacks *def;
- int index;
- int size;
- Query **disjuncts;
-} DisjunctionQuery;
-
-static void DisjunctionQuery_reclaim(Query *this) {
- DisjunctionQuery *q = (DisjunctionQuery*) this;
- int i;
- for ( i = 0; i < q->size; i++ ) {
- q->disjuncts[i]->def->reclaim( q->disjuncts[i] );
- }
- free( q->disjuncts );
- free( this );
-}
-
-static int DisjunctionQuery_next(
- Query *this,BindingTable *bt,enum NextState state) {
- DisjunctionQuery *q = (DisjunctionQuery*) this;
- int i = q->index;
- enum NextState s = subsequent;
- switch ( state ) {
- case initial:
- i = 0;
- s = initial;
- case subsequent:
- for ( ; i < q->size; i++ ) {
- if ( q->disjuncts[i]->def->next( q->disjuncts[i], bt, s ) ) {
- q->index = i;
- return 1;
- }
- q->disjuncts[i]->def->next( q->disjuncts[i], bt, restore );
- s = initial;
- }
- q->index = -1;
- return 0;
- case restore:
- if ( i >= 0 ) {
- q->disjuncts[i]->def->next( q->disjuncts[i], bt, restore );
- q->index = -1;
- }
- return 0;
- }
- return 0;
-}
-
-static struct QueryCallbacks DisjunctionQuery_def = {
- .reclaim = DisjunctionQuery_reclaim,
- .next = DisjunctionQuery_next,
-};
-
-Query *Query_or(int n,...) {
- va_list args;
- DisjunctionQuery *q = (DisjunctionQuery *)
- malloc( sizeof( DisjunctionQuery ) );
- (*q) = (DisjunctionQuery) {
- .def = &DisjunctionQuery_def,
- .index = -1,
- .size = n,
- .disjuncts = (Query**) malloc( n * sizeof( Query* ) ),
- };
- int i;
- va_start( args, n );
- for ( i = 0; i < n; i++ ) {
- q->disjuncts[i] = va_arg( args, Query* );
+ if ( Query_next( q, bt, initial ) && consequent( bt, data ) ) {
+ while ( Query_next( q, bt, subsequent ) && consequent( bt, data ) );
}
- va_end( args );
- return (Query*) q;
+ (void) Query_next( q, bt, restore );
}
-/* ==================== Relation access ==================== */
+/* ==================== Snapshotting a query ==================== */
/**
- * RelationQuery represents a ground query on a relation.
- * \extends Query
- * \related Query
+ * The data used for bindings capture in snapshotting.
*/
-typedef struct {
- struct QueryCallbacks *def;
- Relation *rel;
- VectorIndex index;
+struct Query_snapshot_data {
Tuple *names;
- Tuple *values;
- Tuple *saved;
-} RelationQuery;
-
-// Release any memory.
-static void RelationQuery_reclaim(Query *this) {
- RelationQuery *q = (RelationQuery*) this;
- free( q->saved );
- free( this );
-}
-
-// Make names have given values and return 1. If any name has a
-// different value then return 0. Values are strings.
-static int RelationQuery_next(
- Query *this,BindingTable *bt,enum NextState state) {
- RelationQuery *q = (RelationQuery*) this;
- int arity = ((TupleSchema*) q->rel->content.type)->arity;
- VectorIndex index = q->index + 1;
- switch ( state ) {
- case initial:
- if ( q->saved == 0 ) {
- q->saved = Tuple_clone( arity, q->names );
- } else {
- memcpy( q->saved, q->names, arity * sizeof( void* ) );
- }
- BindingTable_deref( bt, arity, q->saved );
- index = 0;
- // Fall through
- case subsequent:
- for ( ; index < q->rel->content.table.size; index++ ) {
- Tuple *values = Relation_next( q->rel, &index, q->values );
- if ( values ) {
- q->index = index;
- AssignmentQuery_assign( bt, arity, q->names, values, 1 );
- return 1;
- }
- }
- case restore:
- if ( q->saved ) {
- AssignmentQuery_assign( bt, arity, q->names, q->saved, 1 );
- free( q->saved );
- q->saved = 0;
- }
- return 0;
- }
- return 0;
-}
-
-static struct QueryCallbacks RelationQuery_def = {
- .reclaim = RelationQuery_reclaim,
- .next = RelationQuery_next
+ Vector *v;
};
-/**
- * Return a Query object representing an Relation of one or more
- * variables.
- */
-Query *Query_Relation(Relation *r,Tuple *names,Tuple *values) {
- RelationQuery *q = (RelationQuery*) malloc( sizeof( RelationQuery ) );
- (*q) = (RelationQuery) {
- .def = &RelationQuery_def,
- .rel = r,
- .names = names,
- .values = values,
- .saved = 0
- };
- return (Query*) q;
+static int Query_snapshot_capture(BindingTable *bt,void *data) {
+ struct Query_snapshot_data *d = (struct Query_snapshot_data*) data;
+ Tuple *values = Tuple_clone( d->names );
+ BindingTable_deref( bt, values );
+ Vector_append( d->v, values );
+ return 1;
}
-void Query_rule(
- Query *q,BindingTable *bt,
- int (*consequent)(BindingTable *bt,void *data),
- void *data )
-{
- if ( q->def->next( q, bt, initial ) && consequent( bt, data ) ) {
- while ( q->def->next( q, bt, subsequent ) && consequent( bt, data ) );
- }
- (void) q->def->next( q, bt, restore );
+int Query_snapshot(Query *q,Tuple *names,Vector *v) {
+ BindingTable *bt = BindingTable_create( 0 );
+ struct Query_snapshot_data data = { .names = names, .v = v };
+ Vector_resize( v, 0, Vector_free_any, 0 );
+ Query_eval( q, bt, Query_snapshot_capture, &data );
+ return v->size;
}
#ifndef Query_H
#define Query_H
-#include <TupleSchema.h>
-#include <Relation.h>
+#include <QueryCallbacks.h>
#include <BindingTable.h>
-
-struct QueryCallbacks;
+#include <Relation.h>
/**
* A Query is an implementation of a generic ABI over relations. It's
struct QueryCallbacks *def;
} Query;
+/**
+ * \brief Trampoline for the callback function to reclaim the Query
+ * memory for a given Query.
+ *
+ * \param this is the specific \ref Query concerned.
+ *
+ * Ground queries recalim their own state memory. Composite
+ * queries first propagate the reclaim call to its components, and
+ * thereafter reclaim their local state memory.
+ */
+extern void Query_reclaim(Query *this);
+
+/**
+ * \brief Trampoline for the callback function to update the Binding
+ * table with a succession of alternative bindings.
+ *
+ * \param this is the specific \ref Query concerned.
+ *
+ * \param bt is the Binding table to set bindings in.
+ *
+ * \param s is the call "sub-command" for the function.
+ *
+ * \returns 1 if a new Binding is provided and 0 otherwise.
+ *
+ * This function is called repeatedly for successively obtaining
+ * the alternative bindings that satisfy the Query. The "initial"
+ * state sub-command tells the function to capture the incoming
+ * BindingTable state so that the function can later restore it
+ * upon the "restore" sub-command. Upon the "initial" command, the
+ * function also sets up the Binding table with its first Binding
+ * alternative. This is followed by a series of "subsequent"
+ * sub-command calls for the function to change the BindingTable
+ * for its succession of Binding alternatives. The function should
+ * return 1 after any successful Binding setup, and return 0 when
+ * it cannot setup any (more) Binding.
+ */
+extern int Query_next(Query *this,BindingTable *bt,enum NextState state);
+
+/**
+ * \brief Trampoline for the callback function that adds its binding
+ * names to the hashvector.
+ *
+ * \param this is the query concerned.
+ *
+ * \param hv is the HashVector for collating names.
+ */
+extern void Query_variables(Query *this,HashVector *hv);
+
+
+
+
/**
* \brief Creates an assignment Query.
*
*
* \related Query
*/
-extern void Query_rule(
+extern void Query_eval(
Query *q,BindingTable *bt,
int (*consequent)(BindingTable *bt,void *data),
void *data );
+/**
+ * \brief Collect all bindings of query.
+ *
+ * \param q is the query to enumerate.
+ *
+ * \param names is the binding names to track.
+ *
+ * \param results is the result store of bindings for the names.
+ *
+ * \returns the number of results.
+ *
+ * This function evaluates the query for one round of bindings, and
+ * stores their value \ref Tuple Tuples in the given vector. The given
+ * vector is first cleared, and any item is reclaimed with \b free.
+ * Correspondingly the binding \ref Tuple Tuples are allocated with \b
+ * malloc for the caller to reclaim (possibly via a successive call to
+ * this function).
+ */
+//extern int Query_snapshot(Query *q,Tuple *names,Vector *v);
+
+/**
+ * \brief Creates an NotQuery.
+ *
+ * \param query is the query to negate.
+ *
+ * \related Query
+ */
+extern Query *Query_not(Query *values);
+
#endif
#ifndef QueryCallbacks_H
#define QueryCallbacks_H
-typedef struct HashVector HashVector;
-typedef struct Query Query;
-typedef struct BindingTable BindingTable;;
+#include <BindingTable.h>
+
+typedef struct Query Query; // forward
enum NextState {
/**
* thereafter reclaim their local state memory.
*/
void (*reclaim)(Query *this);
+
/**
* \brief Callback function to update the Binding table with a
* succession of alternative bindings.
* it cannot setup any (more) Binding.
*/
int (*next)(Query *this,BindingTable *bt,enum NextState state);
+
/**
* \brief This callback function adds its binding names to the
* hashvector.
int Relation_add_constraint(Relation *r,...) {
va_list ap;
TupleSchema *ts = (TupleSchema *) r->content.type;
- Tuple *columns = (Tuple*) malloc(
- sizeof( Tuple ) + ts->arity * sizeof( void* ) );
+ Tuple *columns = Tuple_clone( ts->columns );
int i = 0;
va_start( ap, r );
- for ( ; i < ts->arity; i++ ) {
- if ( va_arg( ap, int ) ) {
- columns->elements[i] = ts->columns->elements[i];
+ for ( ; i < columns->size; i++ ) {
+ if ( va_arg( ap, int ) == 0 ) {
+ columns->elements[i] = 0;
}
}
va_end( ap );
- ts = TupleSchema_create( ts->arity, columns );
+ ts = TupleSchema_create( columns );
i = (int) r->constraints.size;
Vector_append(
&r->constraints,
--- /dev/null
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <RelationQuery.h>
+
+// Release any memory.
+static void RelationQuery_reclaim(Query *this) {
+ RelationQuery *q = (RelationQuery*) this;
+ free( q->saved );
+ free( this );
+}
+
+// Make names have given values and return 1. If any name has a
+// different value then return 0. Values are strings.
+static int RelationQuery_next(
+ Query *this,BindingTable *bt,enum NextState state) {
+ RelationQuery *q = (RelationQuery*) this;
+ VectorIndex index = q->index + 1;
+ switch ( state ) {
+ case initial:
+ if ( q->saved == 0 ) {
+ q->saved = Tuple_clone( q->names );
+ } else {
+ memcpy( q->saved, q->names, q->names->size * sizeof( void* ) );
+ }
+ BindingTable_deref( bt, q->saved );
+ index = 0;
+ // Fall through
+ case subsequent:
+ for ( ; index < q->rel->content.table.size; index++ ) {
+ Tuple *values = Relation_next( q->rel, &index, q->values );
+ if ( values ) {
+ q->index = index;
+ BindingTable_set_all( bt, q->names, values, 1 );
+ return 1;
+ }
+ }
+ case restore:
+ if ( q->saved ) {
+ BindingTable_set_all( bt, q->names, q->saved, 1 );
+ free( q->saved );
+ q->saved = 0;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+static void RelationQuery_variables(Query *this,HashVector *hv) {
+ RelationQuery *q = (RelationQuery*) this;
+ unsigned long i;
+ for ( i = 0; i < q->names->size; i++ ) {
+ HashVector_add( hv, q->names->elements[i] );
+ }
+}
+
+static struct QueryCallbacks RelationQuery_def = {
+ .reclaim = RelationQuery_reclaim,
+ .next = RelationQuery_next,
+ .variables = RelationQuery_variables
+};
+
+/**
+ * Return a Query object representing an Relation of one or more
+ * variables.
+ */
+Query *Query_relation(Relation *r,Tuple *names,Tuple *values) {
+ RelationQuery *q = (RelationQuery*) malloc( sizeof( RelationQuery ) );
+ (*q) = (RelationQuery) {
+ .def = &RelationQuery_def,
+ .rel = r,
+ .names = names,
+ .values = values,
+ .saved = 0
+ };
+ return (Query*) q;
+}
+
--- /dev/null
+#ifndef RelationQuery_H
+#define RelationQuery_H
+
+#include <Relation.h>
+#include <Query.h>
+
+/**
+ * RelationQuery represents a ground query on a relation.
+ * \extends Query
+ * \related Query
+ */
+typedef struct {
+ struct QueryCallbacks *def;
+ Relation *rel;
+ VectorIndex index;
+ Tuple *names;
+ Tuple *values;
+ Tuple *saved;
+} RelationQuery;
+
+#endif
--- /dev/null
+#ifndef StackVector_H
+#define StackVector_H
+
+/**
+ * A StackVector is a management layer around a single_index_level
+ * Vector to allow it to have extra memory allocated
+ */
+#endif
#include <string.h>
#include <Tuple.h>
-Tuple *Tuple_calloc(int arity) {
+// Allocate zero-ed
+Tuple *Tuple_calloc(unsigned long arity) {
Tuple *t = (Tuple *) malloc( sizeof( Tuple ) + arity * sizeof( void* ) );
- t->types = 0;
+ t->size = arity;
memset( t->elements, 0, arity * sizeof( void* ) );
return t;
}
-// Allocate
+// Allocate with values
Tuple *Tuple_create(int arity,...) {
va_list ap;
int i;
Tuple *t = (Tuple *) malloc( sizeof( Tuple ) + arity * sizeof( void* ) );
- t->types = 0; // unknown types
+ t->size = arity;
va_start( ap, arity );
for ( i = 0; i < arity; i++ ) {
t->elements[i] = va_arg( ap, void* );
return t;
}
-Tuple *Tuple_clone(int arity,Tuple *t) {
- Tuple *ct = (Tuple *)malloc( sizeof( Tuple ) + arity * sizeof( void* ) );
- memcpy( ct, t, arity * sizeof( void* ) );
+// Duplicate
+Tuple *Tuple_clone(Tuple *t) {
+ unsigned long size = t->size * sizeof( void* );
+ Tuple *ct = (Tuple *) malloc( sizeof( Tuple ) + size );
+ memcpy( ct, t, size );
return ct;
}
-
-#if 0
-unsigned long Tuple_mask(int arity,Tuple *t) {
- unsigned long mask = 0;
- while ( arity-- > 0 ) {
- mask <<= 1;
- if ( t->elements[ arity ] ) {
- mask |= 1;
- }
- }
- return mask;
-}
-#endif
* A Tuple is a "self typed" array of elements.
*/
typedef struct {
- TupleSchema *types;
+ /**
+ * The number of elements.
+ */
+ unsigned long size;
+ /**
+ * Base address for element pointers, which thus follow this
+ * struct in memory.
+ */
void *elements[];
} Tuple;
*
* \related Tuple
*/
-extern Tuple *Tuple_calloc(int arity);
+extern Tuple *Tuple_calloc(unsigned long arity);
/**
* \brief Create a tuple as a clone of a given tuple.
*
* \related Tuple
*/
-extern Tuple *Tuple_clone(int arity,Tuple *t);
+extern Tuple *Tuple_clone(Tuple *t);
#endif
Tuple *kp = (Tuple*) key;
int i = 0;
unsigned long value = 5381;
- for ( ; i < def->arity; i++ ) {
+ for ( ; i < def->columns->size; i++ ) {
if ( columns[i] ) {
value <<= 3;
if ( kp->elements[i] ) {
Tuple *kp = (Tuple*) key;
Tuple *tp = (Tuple*) item;
int i = 0;
- for ( ; i < def->arity; i++ ) {
+ for ( ; i < def->columns->size; i++ ) {
if ( columns[i] && kp->elements[i] ) {
if ( columns[i]->haskey(
columns[i], tp->elements[i], kp->elements[i] ) == 0 ) {
TupleSchema *def = (TupleSchema *) this;
ItemKeyFun **columns = (ItemKeyFun**) def->columns->elements;
Tuple *tp = (Tuple*) item;
- Tuple *key = Tuple_clone( def->arity, tp );
+ Tuple *key = Tuple_clone( tp );
int i;
- for ( i = 0; i < def->arity; i++ ) {
+ for ( i = 0; i < def->columns->size; i++ ) {
if ( columns[i] ) {
key->elements[i] = columns[i]->itemkey(
columns[i], tp->elements[i] );
ItemKeyFun **columns = (ItemKeyFun**) def->columns->elements;
Tuple *kp = (Tuple*) key;
int i;
- for ( i = 0; i < def->arity; i++ ) {
+ for ( i = 0; i < def->columns->size; i++ ) {
if ( columns[i] ) {
columns[i]->releasekey( columns[i], kp->elements[i] );
}
Tuple *t = (Tuple*) item;
char *x = "<";
int a, i;
- for ( i = 0; i < def->arity; i++ ) {
+ for ( i = 0; i < def->columns->size; i++ ) {
OUT( snprintf( buffer, limit, x ) );
x = ",";
OUT( columns[i]->tostring(
.tostring = TupleSchema_tostring
};
-TupleSchema *TupleSchema_create(int arity,Tuple *columns) {
+TupleSchema *TupleSchema_create(Tuple *columns) {
TupleSchema *ts = (TupleSchema*) malloc( sizeof( TupleSchema ) );
(*ts) = (TupleSchema) {
.base = TupleSchema_callbacks,
- .arity = arity,
.columns = columns
};
return ts;
// Duplicate a TupleSchema with optionally some columns reset.
TupleSchema *TupleSchema_mask(TupleSchema *schema,...) {
TupleSchema *masked = COPY(TupleSchema,schema);
- masked->columns = Tuple_clone( schema->arity, schema->columns );
+ masked->columns = Tuple_clone( schema->columns );
va_list ap;
int i;
va_start( ap, schema );
for ( ;; ) {
i = va_arg( ap, int );
- if ( i < 0 || i >= schema->arity ) {
+ if ( i < 0 || i >= schema->columns->size ) {
break;
}
masked->columns->elements[i] = 0;
*/
ItemKeyFun base;
- /**
- * This is the number of columns in a tuple.
- */
- int arity;
-
/**
* This points to tuple whose elements is array of pointers to the
* tuple item domains as represented by their associated
*
* \related TupleSchema
*/
-extern TupleSchema *TupleSchema_create(int arity,Tuple *columns);
+extern TupleSchema *TupleSchema_create(Tuple *columns);
/**
* Copy the given TupleSchema into a new TupleSchema with some columns
* This is the collection of bindings for the tracked names being
* gained at the latest evaluation.
*/
- HashVector gained;
+ Vector gained;
/**
* This is the collection of bindings for the tracked names being
* last at the latest evaluation.
Tuple *t = Tuple_calloc( hv->fill );
VectorIndex index = 0;
for ( ; index < hv->table.size; index++ ) {
- t->elements[ index ] = &stringitem;
+ t->elements[ index ] = HashVector_next( hv, &index );
}
return t;
}
+static void View_reclaim(Query *this) {
+ View *v = (View*) this;
+ TupleSchema *ts = (TupleSchema*) gained->type;
+ Vector_resize( &v->gained, 0, Vector_clear_any, 0 );
+ Vector_resize( &v->lost, 0, Vector_clear_any, 0 );
+ free( ts->columns );
+ free( ts );
+ free( this );
+}
+
static QueryCallbacks View_def = {
- .reclaim = 0,
+ .reclaim = View_reclaim,
.next = 0,
.variables = 0
};
.fill = 0, .holes = 0, .type = (ItemKeyFun*) ts
}
};
+ Vector_resize( &hv, 0, Vector_clear_any, 0 );
return (Query*) vq;
}
#ifndef View_H
#define View_H
+/**
+ * \brief Create a ViewQuery for monitoring the binding stream of a
+ * sub query.
+ *
+ * \param q is the sub query to monitor.
+ *
+ * \returns the ViewQuery qua Query.
+ *
+ * A ViewQuery tracks the binding succession of a sub query, through
+ * each initial, repeated subsequent and final restore sequence, in
+ * order to tell if a binding is new or lost relative to the previous
+ * sequence. The ViewQuery is
+ */
+extern Query *View_create(Query *q);
#endif