refactoring
[rrq/rrqmisc.git] / vector / Query.c
index c13ecde73e418e4c399f190fc7ad4f370e46809f..94a39d744a2b288638d107501edbc7ab088c5b1c 100644 (file)
-#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;
 }