-#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;
}