/* SDB - select data from the database */

#include "sdbio.h"

extern int dbv_token;
extern char dbv_tstring[];
extern int dbv_tvalue;

/* db_select - select a set of tuples from a set of relations */
struct sel *db_select(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
  char *fmt;
{
    struct sel *slptr;

    /* check for a command line */
    if (fmt != NULL)
        db_scan(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);

    /* allocate a sel structure */
    if ((slptr = malloc(sizeof(struct sel))) == NULL)
        return (db_nerror(INSMEM));

    /* initialize the structure */
    slptr->sl_rels = NULL;
    slptr->sl_attrs = NULL;
    slptr->sl_where = NULL;
    slptr->sl_bindings = NULL;

    /* parse the list of selected attributes */
    if (!get_sattrs(slptr)) {
        db_done(slptr);
        return (NULL);
    }

    /* check for "from" clause */
    if (db_token() == FROM) {
        db_ntoken();
        if (!get_srels(slptr)) {
            db_done(slptr);
            return (NULL);
        }
    }
    else {
        if (!srelation(slptr,"sdbcur",NULL)) {
            db_done(slptr);
            return (NULL);
        }
    }

    /* check the list of selected attributes */
    if (!check_attrs(slptr)) {
        db_done(slptr);
        return (NULL);
    }

    /* check for the existance of a "where" clause */
    if (db_token() == WHERE) {
        db_ntoken();

        /* parse the boolean expression */
        if (!db_compile(slptr)) {
            db_done(slptr);
            return (FALSE);
        }
    }

    /* return the new selection structure */
    return (slptr);
}

/* db_retrieve - retrieve a set of tuples from a set of relations */
struct sel *db_retrieve(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
  char *fmt;
{
    struct sel *slptr;

    /* check for a command line */
    if (fmt != NULL)
        db_scan(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);

    /* allocate a sel structure */
    if ((slptr = malloc(sizeof(struct sel))) == NULL)
        return (db_nerror(INSMEM));

    /* initialize the structure */
    slptr->sl_rels = NULL;
    slptr->sl_attrs = NULL;
    slptr->sl_where = NULL;
    slptr->sl_bindings = NULL;

    /* check for selected relations clause */
    if (db_token() == ID) {
        if (!get_srels(slptr)) {
            db_done(slptr);
            return (NULL);
        }
    }
    else {
        if (!srelation(slptr,"sdbcur",NULL)) {
            db_done(slptr);
            return (NULL);
        }
    }

    /* check the list of selected attributes */
    if (!check_attrs(slptr)) {
        db_done(slptr);
        return (NULL);
    }

    /* check for the existance of a "where" clause */
    if (db_token() == WHERE) {
        db_ntoken();

        /* parse the boolean expression */
        if (!db_compile(slptr)) {
            db_done(slptr);
            return (FALSE);
        }
    }

    /* return the new selection structure */
    return (slptr);
}

/* db_done(slptr) - finish a selection */
db_done(slptr)
  struct sel *slptr;
{
    struct sattr *saptr,*nxtsa;
    struct srel *srptr,*nxtsr;
    struct binding *bdptr,*nxtbd;

    /* free the selected attribute blocks */
    for (saptr = slptr->sl_attrs; saptr != NULL; saptr = nxtsa) {
        nxtsa = saptr->sa_next;
        if (saptr->sa_rname != NULL)
            free(saptr->sa_rname);
        free(saptr->sa_aname);
        if (saptr->sa_name != NULL)
            free(saptr->sa_name);
        free(saptr);
    }

    /* close the scans and free the selected relation blocks */
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = nxtsr) {
        nxtsr = srptr->sr_next;
        if (srptr->sr_name != NULL)
            free(srptr->sr_name);
        db_rclose(srptr->sr_scan);
        free(srptr);
    }

    /* free the where clause */
    db_fcode(slptr);

    /* free the user bindings */
    for (bdptr = slptr->sl_bindings; bdptr != NULL; bdptr = nxtbd) {
        nxtbd = bdptr->bd_next;
        free(bdptr);
    }

    /* free the selection structure */
    free(slptr);
}

/* db_fetch(slptr) - fetch the next tuple from a selection */
int db_fetch(slptr)
  struct sel *slptr;
{
    struct srel *srptr;
    struct binding *bdptr;

    /* clear the update flags */
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next)
        srptr->sr_update = FALSE;

    /* find a matching tuple */
    while (process(slptr->sl_rels))
        if (db_interpret(slptr)) {
            for (bdptr = slptr->sl_bindings; bdptr != NULL; bdptr = bdptr->bd_next)
                db_aget(bdptr->bd_attr,bdptr->bd_vtuple,bdptr->bd_vuser);
            return (TRUE);
        }

    /* no matches, failure return */
    return (FALSE);
}

/* db_update - update modified tuples */
int db_update(slptr)
  struct sel *slptr;
{
    struct srel *srptr;

    /* check each selected relation for updates */
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next)
        if (srptr->sr_update)
            if (!db_rupdate(srptr->sr_scan))
                return (FALSE);

    /* return successfully */
    return (TRUE);
}

/* db_store - store tuples */
int db_store(slptr)
  struct sel *slptr;
{
    struct srel *srptr;

    /* check each selected relation for stores */
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next)
        if (srptr->sr_update)
            if (!db_rstore(srptr->sr_scan))
                return (FALSE);

    /* return successfully */
    return (TRUE);
}

/* db_bind - bind a user buffer to the value of an attribute */
int db_bind(slptr,rname,aname,avalue)
  struct sel *slptr; char *rname,*aname,*avalue;
{
    struct binding *newbd;
    struct srel *srptr;

    /* allocate and initialize a binding structure */
    if ((newbd = malloc(sizeof(struct binding))) == NULL)
        return (db_ferror(INSMEM));
    newbd->bd_vuser = avalue;

    /* find the attribute */
    if (!find_attr(slptr,rname,aname,&newbd->bd_vtuple,&srptr,&newbd->bd_attr))
        return (FALSE);

    /* link the new binding into the binding list */
    newbd->bd_next = slptr->sl_bindings;
    slptr->sl_bindings = newbd;

    /* return successfully */
    return (TRUE);
}

/* db_get - get the value of an attribute */
int db_get(slptr,rname,aname,avalue)
  struct sel *slptr; char *rname,*aname,*avalue;
{
    struct srel *srptr;
    struct attribute *aptr;
    char *vptr;

    /* find the attribute */
    if (!find_attr(slptr,rname,aname,&vptr,&srptr,&aptr))
        return (FALSE);

    /* get the attribute value */
    db_aget(aptr,vptr,avalue);

    /* return successfully */
    return (TRUE);
}

/* db_put - put the value of an attribute */
int db_put(slptr,rname,aname,avalue)
  struct sel *slptr; char *rname,*aname,*avalue;
{
    struct srel *srptr;
    struct attribute *aptr;
    char *vptr;

    /* find the attribute */
    if (!find_attr(slptr,rname,aname,&vptr,&srptr,&aptr))
        return (FALSE);

    /* put the attribute value */
    db_aput(aptr,vptr,avalue);

    /* mark the tuple as updated */
    srptr->sr_update = TRUE;

    /* return successfully */
    return (TRUE);
}

/* db_sattr - get selected attribute type, pointer, and length */
int db_sattr(slptr,rname,aname,ptype,pptr,plen)
  struct sel *slptr; char *rname,*aname;
  int *ptype; char **pptr; int *plen;
{
    struct srel *srptr;
    struct attribute *aptr;

    if (!find_attr(slptr,rname,aname,pptr,&srptr,&aptr))
        return (FALSE);
    *ptype = aptr->at_type;
    *plen = aptr->at_size;
    return (TRUE);
}

/* get_sattrs(slptr) - get selected attributes */
static get_sattrs(slptr)
  struct sel *slptr;
{
    struct sattr *newsattr,*lastsattr;

    /* check for "*" or blank field meaning all attributes are selected */
    if (db_token() == '*') {
        db_ntoken();
        return (TRUE);
    }
    else if (db_token() != ID)
        return (TRUE);

    /* parse a list of attribute names */
    lastsattr = NULL;
    while (TRUE) {

        /* get attribute name */
        if (db_ntoken() != ID)
            return (db_ferror(SYNTAX));

        /* allocate a selected attribute structure */
        if ((newsattr = malloc(sizeof(struct sattr))) == NULL)
            return (db_ferror(INSMEM));

        /* initialize the selected attribute structure */
        newsattr->sa_next = NULL;

        /* save the attribute name */
        if ((newsattr->sa_aname = malloc(strlen(dbv_tstring)+1)) == NULL) {
            free(newsattr);
            return (db_ferror(INSMEM));
        }
        strcpy(newsattr->sa_aname,dbv_tstring);

        /* check for "." meaning "<rel-name>.<att-name>" */
        if (db_token() == '.') {
            db_ntoken();

            /* the previous ID was really the relation name */
            newsattr->sa_rname = newsattr->sa_aname;

            /* check for attribute name */
            if (db_ntoken() != ID) {
                free(newsattr->sa_aname); free(newsattr);
                return (db_ferror(SYNTAX));
            }

            /* save the attribute name */
            if ((newsattr->sa_aname = malloc(strlen(dbv_tstring)+1)) == NULL) {
                free(newsattr->sa_aname); free(newsattr);
                return (db_ferror(INSMEM));
            }
            strcpy(newsattr->sa_aname,dbv_tstring);
        }
        else
            newsattr->sa_rname = NULL;

        /* check for alternate attribute name */
        if (db_token() == ID) {
            db_ntoken();

            /* allocate space for the alternate name */
            if ((newsattr->sa_name = malloc(strlen(dbv_tstring)+1)) == NULL) {
                if (newsattr->sa_rname != NULL)
                    free(newsattr->sa_rname);
                free(newsattr->sa_aname);
                free(newsattr);
                return (db_ferror(INSMEM));
            }
            strcpy(newsattr->sa_name,dbv_tstring);
        }
        else
            newsattr->sa_name = NULL;

        /* link the selected attribute structure into the list */
        if (lastsattr == NULL)
            slptr->sl_attrs = newsattr;
        else
            lastsattr->sa_next = newsattr;
        lastsattr = newsattr;

        /* check for more attributes */
        if (db_token() != ',')
            break;
        db_ntoken();
    }

    /* return successfully */
    return (TRUE);
}

/* get_srels(slptr) - get selected relations */
static get_srels(slptr)
  struct sel *slptr;
{
    char rname[KEYWORDMAX+1],*aname;

    /* get the list of selected relations */
    while (TRUE) {

        /* check for relation name */
        if (db_ntoken() != ID)
            return (db_ferror(SYNTAX));
        strcpy(rname,dbv_tstring);

        /* check for alternate relation name */
        if (db_token() == ID) {
            db_ntoken();
            aname = dbv_tstring;
        }
        else
            aname = NULL;

        /* add the relation name to the list */
        if (!srelation(slptr,rname,aname))
            return (FALSE);

        /* check for more selected relations */
        if (db_token() != ',')
            break;
        db_ntoken();
    }

    /* return successfully */
    return (TRUE);
}

/* srelation - select a relation */
static srelation(slptr,rname,aname)
  struct sel *slptr; char *rname,*aname;
{
    struct srel *srptr,*newsrel;

    /* allocate a new selected relation structure */
    if ((newsrel = malloc(sizeof(struct srel))) == NULL)
        return (db_ferror(INSMEM));

    /* initialize the new selected relation structure */
    newsrel->sr_ctuple = FALSE;
    newsrel->sr_update = FALSE;
    newsrel->sr_next = NULL;

    /* open the relation */
    if ((newsrel->sr_scan = db_ropen(rname)) == NULL) {
        free(newsrel);
        return (FALSE);
    }

    /* check for alternate relation name */
    if (aname != NULL) {

        /* allocate space for the alternate name */
        if ((newsrel->sr_name = malloc(strlen(aname)+1)) == NULL) {
            free(newsrel);
            return (db_ferror(INSMEM));
        }
        strcpy(newsrel->sr_name,aname);
    }
    else
        newsrel->sr_name = NULL;

    /* find the end of the list of relation names */
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next)
        if (srptr->sr_next == NULL)
            break;

    /* link the new selected relation structure into the list */
    if (srptr == NULL)
        slptr->sl_rels = newsrel;
    else
        srptr->sr_next = newsrel;

    /* return successfully */
    return (TRUE);
}

/* check_attrs(slptr) - check the list of selected attributes */
static int check_attrs(slptr)
  struct sel *slptr;
{
    struct sattr *saptr;

    /* check for all attributes selected */
    if (slptr->sl_attrs == NULL)
        return (all_attrs(slptr));

    /* check each selected attribute */
    for (saptr = slptr->sl_attrs; saptr != NULL; saptr = saptr->sa_next)
        if (!find_attr(slptr,saptr->sa_rname,saptr->sa_aname,
                        &saptr->sa_aptr,&saptr->sa_srel,&saptr->sa_attr))
            return (FALSE);

    /* return successfully */
    return (TRUE);
}

/* all_attrs(slptr) - create a list of all attributes */
static int all_attrs(slptr)
  struct sel *slptr;
{
    struct sattr *newsattr,*lastsattr;
    struct srel *srptr;
    struct attribute *aptr;
    int i,astart;

    /* loop through each selected relation */
    lastsattr = NULL;
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next) {

        /* loop through each attribute within the relation */
        astart = 1;
        for (i = 0; i < NATTRS; i++) {

            /* get a pointer to the current attribute */
            aptr = &srptr->sr_scan->sc_relation->rl_header.hd_attrs[i];

            /* check for last attribute */
            if (aptr->at_name[0] == 0)
                break;

            /* allocate a new selected attribute structure */
            if ((newsattr = malloc(sizeof(struct sattr))) == NULL)
                return (db_ferror(INSMEM));

            /* initialize the new selected attribute structure */
            newsattr->sa_name = NULL;
            newsattr->sa_srel = srptr;
            newsattr->sa_aptr = srptr->sr_scan->sc_tuple + astart;
            newsattr->sa_attr = aptr;
            newsattr->sa_next = NULL;

            /* save the relation name */
            if ((newsattr->sa_rname = malloc(RNSIZE+1)) == NULL) {
                free(newsattr);
                return (db_ferror(INSMEM));
            }
            strncpy(newsattr->sa_rname,
                    srptr->sr_scan->sc_relation->rl_name,
                    RNSIZE);
            newsattr->sa_rname[RNSIZE] = 0;

            /* save the attribute name */
            if ((newsattr->sa_aname = malloc(ANSIZE+1)) == NULL) {
                free(newsattr->sa_rname);
                free(newsattr);
                return (db_ferror(INSMEM));
            }
            strncpy(newsattr->sa_aname,
                    srptr->sr_scan->sc_relation->rl_header.hd_attrs[i].at_name,
                    ANSIZE);
            newsattr->sa_aname[ANSIZE] = 0;

            /* link the selected attribute into the list */
            if (lastsattr == NULL)
                slptr->sl_attrs = newsattr;
            else
                lastsattr->sa_next = newsattr;
            lastsattr = newsattr;

            /* update the attribute start */
            astart += aptr->at_size;
        }
    }

    /* return successfully */
    return (TRUE);
}

/* find_attr - find a named attribute */
static int find_attr(slptr,rname,aname,paptr,psrel,pattr)
  struct sel *slptr; char *rname,*aname;
  char **paptr; struct attribute **pattr;
{
    /* check for unqualified or qualified attribute names */
    if (rname == NULL)
        return (uattr(slptr,aname,paptr,psrel,pattr));
    else
        return (qattr(slptr,rname,aname,paptr,psrel,pattr));
}

/* uattr - find an unqualified attribute name */
static int uattr(slptr,aname,paptr,psrel,pattr)
  struct sel *slptr; char *aname;
  char **paptr; struct srel **psrel; struct attribute **pattr;
{
    struct srel *srptr;
    struct attribute *aptr;
    int i,astart;

    /* loop through each selected relation */
    *pattr = NULL;
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next) {

        /* loop through each attribute within the relation */
        astart = 1;
        for (i = 0; i < NATTRS; i++) {

            /* get a pointer to the current attribute */
            aptr = &srptr->sr_scan->sc_relation->rl_header.hd_attrs[i];

            /* check for last attribute */
            if (aptr->at_name[0] == 0)
                break;

            /* check for attribute name match */
            if (db_sncmp(aname,aptr->at_name,ANSIZE) == 0) {
                if (*pattr != NULL)
                    return (db_ferror(ATAMBG));
                *paptr = srptr->sr_scan->sc_tuple + astart;
                *psrel = srptr;
                *pattr = aptr;
            }

            /* update the attribute start */
            astart += aptr->at_size;
        }
    }

    /* check whether attribute was found */
    if (*pattr == NULL)
        return (db_ferror(ATUNDF));

    /* return successfully */
    return (TRUE);
}

/* qattr - find a qualified attribute name */
static int qattr(slptr,rname,aname,paptr,psrel,pattr)
  struct sel *slptr; char *rname,*aname;
  char **paptr; struct srel **psrel; struct attribute **pattr;
{
    struct srel *srptr;
    struct attribute *aptr;
    char *crname;
    int i,astart;

    /* loop through each selected relation */
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next) {

        /* get relation name */
        if ((crname = srptr->sr_name) == NULL)
            crname = srptr->sr_scan->sc_relation->rl_name;

        /* check for relation name match */
        if (db_sncmp(rname,crname,RNSIZE) == 0) {

            /* loop through each attribute within the relation */
            astart = 1;
            for (i = 0; i < NATTRS; i++) {

                /* get a pointer to the current attribute */
                aptr = &srptr->sr_scan->sc_relation->rl_header.hd_attrs[i];

                /* check for last attribute */
                if (aptr->at_name[0] == 0)
                    break;

                /* check for attribute name match */
                if (db_sncmp(aname,aptr->at_name,ANSIZE) == 0) {
                    *paptr = srptr->sr_scan->sc_tuple + astart;
                    *psrel = srptr;
                    *pattr = aptr;
                    return (TRUE);
                }

                /* update the attribute start */
                astart += aptr->at_size;
            }

            /* attribute name not found */
            return (db_ferror(ATUNDF));
        }
    }

    /* relation name not found */
    return (db_ferror(RLUNDF));
}

/* process(srptr) - process each tuple in a relation cross-product */
static int process(srptr)
  struct srel *srptr;
{
    /* always get a new tuple if this is the last relation in the list */
    if (srptr->sr_next == NULL) {

        /* check for beginning of new scan */
        if (!srptr->sr_ctuple)
            db_rbegin(srptr->sr_scan);

        /* return the next tuple in the relation */
        return (srptr->sr_ctuple = db_rfetch(srptr->sr_scan));
    }

    /* check for beginning of new scan */
    if (!srptr->sr_ctuple) {
        db_rbegin(srptr->sr_scan);

        /* get the first tuple */
        if (!db_rfetch(srptr->sr_scan))
            return (FALSE);
    }

    /* look for a match with the remaining relations in list */
    while (!process(srptr->sr_next))

        /* get the next tuple in the scan */
        if (!db_rfetch(srptr->sr_scan))
            return (srptr->sr_ctuple = FALSE);

    /* found a match at this level */
    return (srptr->sr_ctuple = TRUE);
}

/* db_aget - get the value of an attribute */
db_aget(aptr,vptr,avalue)
  struct attribute *aptr; char *vptr,*avalue;
{
    int i;

    /* get the attribute value */
    for (i = 0; i < aptr->at_size; i++)
        *avalue++ = vptr[i];
    *avalue = EOS;
}

/* db_aput - put the value of an attribute */
db_aput(aptr,vptr,avalue)
  struct attribute *aptr; char *vptr,*avalue;
{
    int i;

    /* initialize counter */
    i = 0;

    /* right justify numbers */
    if (aptr->at_type == TNUM)
        for (; i < aptr->at_size - strlen(avalue); i++)
            vptr[i] = ' ';

    /* put the attribute value */
    for (; i < aptr->at_size; i++)
        if (*avalue == 0)
            vptr[i] = 0;
        else
            vptr[i] = *avalue++;
}

