src/utils/raSqlQuery/raSqlQuery.c 1.10
1.10 2009/11/20 07:41:56 kent
Adding in merge option. Supporting 'not like' operation. Splitting into modules.
Index: src/utils/raSqlQuery/raSqlQuery.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/utils/raSqlQuery/raSqlQuery.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -b -B -U 4 -r1.9 -r1.10
--- src/utils/raSqlQuery/raSqlQuery.c 20 Nov 2009 06:12:40 -0000 1.9
+++ src/utils/raSqlQuery/raSqlQuery.c 20 Nov 2009 07:41:56 -0000 1.10
@@ -8,8 +8,10 @@
#include "ra.h"
#include "localmem.h"
#include "tokenizer.h"
#include "sqlNum.h"
+#include "raRecord.h"
+#include "rql.h"
static char const rcsid[] = "$Id$";
void usage()
@@ -50,974 +52,67 @@
{"skipMissing", OPTION_BOOLEAN},
{NULL, 0},
};
-struct raField
-/* A single field. */
- {
- struct raField *next; /* Next in list. */
- char *name; /* Field name. */
- char *val; /* Field value. */
- };
-
-struct raRecord
-/* A single RA record. */
- {
- struct raRecord *next; /* Next in list. */
- struct raField *key; /* Key field if any. */
- struct raField *fieldList; /* List of fields. */
- };
-
-enum rqlParseOp
- {
- rqlParseUnknown, /* Should not occur */
- rqlParseLiteral, /* Literal string or number. */
- rqlParseSymbol, /* A symbol name. */
- rqlParseEq, /* An equals comparison */
- rqlParseNe, /* A not equals comparison */
-
- rqlParseStringToBoolean,
- rqlParseIntToBoolean,
- rqlParseDoubleToBoolean,
- rqlParseStringToInt,
- rqlParseStringToDouble,
- rqlParseBooleanToInt,
- rqlParseBooleanToDouble,
- rqlParseIntToDouble,
-
- rqlParseUnaryMinusDouble,
-
- rqlParseGt, /* Greater than comparison. */
- rqlParseLt, /* Less than comparison. */
- rqlParseGe, /* Greater than or equals comparison. */
- rqlParseLe, /* Less than or equals comparison. */
- rqlParseLike, /* SQL wildcard compare. */
-
- rqlParseAnd, /* An and */
- rqlParseOr, /* An or */
- };
-
-char *rqlParseOpToString(enum rqlParseOp op)
-/* Return string representation of parse op. */
-{
-switch (op)
- {
- case rqlParseLiteral:
- return "rqlParseLiteral";
- case rqlParseSymbol:
- return "rqlParseSymbol";
- case rqlParseEq:
- return "rqlParseEq";
- case rqlParseNe:
- return "rqlParseNe";
- case rqlParseAnd:
- return "rqlParseAnd";
- case rqlParseOr:
- return "rqlParseOr";
-
- case rqlParseStringToBoolean:
- return "rqlParseStringToBoolean";
- case rqlParseIntToBoolean:
- return "rqlParseIntToBoolean";
- case rqlParseDoubleToBoolean:
- return "rqlParseDoubleToBoolean";
- case rqlParseStringToInt:
- return "rqlParseStringToInt";
- case rqlParseStringToDouble:
- return "rqlParseStringToDouble";
- case rqlParseBooleanToInt:
- return "rqlParseBooleanToInt";
- case rqlParseBooleanToDouble:
- return "rqlParseBooleanToDouble";
- case rqlParseIntToDouble:
- return "rqlParseIntToDouble";
-
- case rqlParseUnaryMinusDouble:
- return "rqlParseUnaryMinusDouble";
-
- case rqlParseGt:
- return "rqlParseGt";
- case rqlParseLt:
- return "rqlParseLt";
- case rqlParseGe:
- return "rqlParseGe";
- case rqlParseLe:
- return "rqlParseLe";
- case rqlParseLike:
- return "rqlParseLike";
-
- default:
- return "rqlParseUnknown";
- }
-}
-
-enum rqlType
-/* A type */
- {
- rqlTypeBoolean = 1,
- rqlTypeString = 2,
- rqlTypeInt = 3,
- rqlTypeDouble = 4,
- };
-
-union rqlVal
-/* Some value of arbirary type that can be of any type corresponding to rqlType */
- {
- boolean b;
- char *s;
- int i;
- double x;
- };
-
-struct rqlEval
-/* Result of evaluation of parse tree. */
- {
- enum rqlType type;
- union rqlVal val;
- };
-
-struct rqlParse
-/* A rql parse-tree. */
- {
- struct rqlParse *next; /* Points to younger sibling if any. */
- struct rqlParse *children; /* Points to oldest child if any. */
- enum rqlParseOp op; /* Operation at this node. */
- enum rqlType type; /* Return type of this operation. */
- union rqlVal val; /* Return value of this operation. */
- };
-
-void rqlValDump(union rqlVal val, enum rqlType type, FILE *f)
-/* Dump out value to file. */
-{
-switch (type)
- {
- case rqlTypeBoolean:
- fprintf(f, "%s", (val.b ? "true" : "false") );
- break;
- case rqlTypeString:
- fprintf(f, "%s", val.s);
- break;
- case rqlTypeInt:
- fprintf(f, "%d", val.i);
- break;
- case rqlTypeDouble:
- fprintf(f, "%f", val.x);
- break;
- }
-}
-
-struct rqlStatement
-/* A parsed out RQL statement */
- {
- char *next; /* Next in list */
- char *command; /* Generally the first word in the statement. */
- struct slName *fieldList; /* List of fields if any. */
- struct rqlParse *whereClause; /* Where clause if any - tokenized. */
- };
-
-void rqlParseDump(struct rqlParse *p, int depth, FILE *f)
-/* Dump out rqlParse tree and children. */
-{
-spaceOut(f, 3*depth);
-fprintf(f, "%s ", rqlParseOpToString(p->op));
-rqlValDump(p->val, p->type, f);
-fprintf(f, "\n");
-struct rqlParse *child;
-for (child = p->children; child != NULL; child= child->next)
- rqlParseDump(child, depth+1, f);
-}
-
-static void expectingGot(struct tokenizer *tkz, char *expecting, char *got)
-/* Print out error message about unexpected input. */
-{
-errAbort("Expecting %s, got %s, line %d of %s", expecting, got, tkz->lf->lineIx,
- tkz->lf->fileName);
-}
-
-static void skipOverRequired(struct tokenizer *tkz, char *expecting)
-/* Make sure that next token is tok, and skip over it. */
-{
-tokenizerMustHaveNext(tkz);
-if (!sameString(tkz->string, expecting))
- expectingGot(tkz, expecting, tkz->string);
-}
-
-
-struct rqlParse *rqlParseExpression(struct tokenizer *tkz);
-/* Parse out a clause, usually a where clause. */
-
-struct rqlParse *rqlParseAtom(struct tokenizer *tkz)
-/* Return low level (symbol or literal) */
-{
-char *tok = tokenizerMustHaveNext(tkz);
-struct rqlParse *p;
-AllocVar(p);
-char c = tok[0];
-if (c == '\'' || c == '"')
- {
- p->op = rqlParseLiteral;
- p->type = rqlTypeString;
- int len = strlen(tok+1);
- p->val.s = cloneStringZ(tok+1, len-1);
- }
-else if (isalpha(c) || c == '_')
- {
- p->op = rqlParseSymbol;
- p->type = rqlTypeString; /* String until promoted at least. */
- p->val.s = cloneString(tok);
- }
-else if (isdigit(c))
- {
- p->op = rqlParseLiteral;
- p->type = rqlTypeInt;
- p->val.i = sqlUnsigned(tok);
- if ((tok = tokenizerNext(tkz)) != NULL)
- {
- if (tok[0] == '.')
- {
- char buf[32];
- tok = tokenizerMustHaveNext(tkz);
- safef(buf, sizeof(buf), "%d.%s", p->val.i, tok);
- p->type = rqlTypeDouble;
- p->val.x = sqlDouble(buf);
- }
- else
- tokenizerReuse(tkz);
- }
- }
-else if (c == '(')
- {
- p = rqlParseExpression(tkz);
- skipOverRequired(tkz, ")");
- }
-else
- {
- errAbort("Unexpected %s line %d of %s", tok, tkz->lf->lineIx, tkz->lf->fileName);
- }
-return p;
-}
-
-enum rqlType commonTypeForBop(enum rqlType left, enum rqlType right)
-/* Return type that will work for a binary operation. */
-{
-if (left == right)
- return left;
-else if (left == rqlTypeDouble || right == rqlTypeDouble)
- return rqlTypeDouble;
-else if (left == rqlTypeInt || right == rqlTypeInt)
- return rqlTypeInt;
-else if (left == rqlTypeBoolean || right == rqlTypeBoolean)
- return rqlTypeBoolean;
-else if (left == rqlTypeString || right == rqlTypeString)
- return rqlTypeString;
-else
- {
- errAbort("Can't find commonTypeForBop");
- return rqlTypeInt;
- }
-}
-
-enum rqlParseOp booleanCastOp(enum rqlType oldType)
-/* Return op to convert oldType to boolean. */
-{
-switch (oldType)
- {
- case rqlTypeString:
- return rqlParseStringToBoolean;
- case rqlTypeInt:
- return rqlParseIntToBoolean;
- case rqlTypeDouble:
- return rqlParseDoubleToBoolean;
- default:
- internalErr();
- return rqlParseUnknown;
- }
-}
-
-enum rqlParseOp intCastOp(enum rqlType oldType)
-/* Return op to convert oldType to int. */
-{
-switch (oldType)
- {
- case rqlTypeString:
- return rqlParseStringToInt;
- case rqlTypeBoolean:
- return rqlParseBooleanToInt;
- default:
- internalErr();
- return rqlParseUnknown;
- }
-}
-
-enum rqlParseOp doubleCastOp(enum rqlType oldType)
-/* Return op to convert oldType to double. */
-{
-switch (oldType)
- {
- case rqlTypeString:
- return rqlParseStringToDouble;
- case rqlTypeBoolean:
- return rqlParseBooleanToDouble;
- case rqlTypeInt:
- return rqlParseIntToDouble;
- default:
- internalErr();
- return rqlParseUnknown;
- }
-}
-
-
-struct rqlParse *rqlParseCoerce(struct rqlParse *p, enum rqlType type)
-/* If p is not of correct type, wrap type conversion node around it. */
-{
-if (p->type == type)
- return p;
-else
- {
- struct rqlParse *cast;
- AllocVar(cast);
- cast->children = p;
- cast->type = type;
- switch (type)
- {
- case rqlTypeBoolean:
- cast->op = booleanCastOp(p->type);
- break;
- case rqlTypeInt:
- cast->op = intCastOp(p->type);
- break;
- case rqlTypeDouble:
- cast->op = doubleCastOp(p->type);
- break;
- default:
- internalErr();
- break;
- }
- return cast;
- }
-}
-
-struct rqlParse *rqlParseUnaryMinus(struct tokenizer *tkz)
-/* Return unary minus sort of parse tree if there's a leading '-' */
-{
-char *tok = tokenizerMustHaveNext(tkz);
-if (tok[0] == '-')
- {
- struct rqlParse *c = rqlParseAtom(tkz);
- c = rqlParseCoerce(c, rqlTypeDouble);
- struct rqlParse *p;
- AllocVar(p);
- p->op = rqlParseUnaryMinusDouble;
- p->type = rqlTypeDouble;
- p->children = c;
- return p;
- }
-else
- {
- tokenizerReuse(tkz);
- return rqlParseAtom(tkz);
- }
-}
-
-boolean eatMatchingTok(struct tokenizer *tkz, char *s)
-/* If next token matches s then eat it and return TRUE */
-{
-char *tok = tokenizerNext(tkz);
-if (tok != NULL && sameString(tok, s))
- return TRUE;
-else
- {
- tokenizerReuse(tkz);
- return FALSE;
- }
-}
-
-struct rqlParse *rqlParseCmp(struct tokenizer *tkz)
-/* Parse out comparison. */
-{
-struct rqlParse *l = rqlParseUnaryMinus(tkz);
-struct rqlParse *p = l;
-char *tok = tokenizerNext(tkz);
-boolean forceString = FALSE;
-if (tok != NULL)
- {
- enum rqlParseOp op = rqlParseUnknown;
- if (sameString(tok, "="))
- {
- op = rqlParseEq;
- }
- else if (sameString(tok, "!"))
- {
- op = rqlParseNe;
- skipOverRequired(tkz, "=");
- }
- else if (sameString(tok, ">"))
- {
- if (eatMatchingTok(tkz, "="))
- op = rqlParseGe;
- else
- op = rqlParseGt;
- }
- else if (sameString(tok, "<"))
- {
- if (eatMatchingTok(tkz, "="))
- op = rqlParseGe;
- else
- op = rqlParseLe;
- }
- else if (sameString(tok, "like"))
- {
- forceString = TRUE;
- op = rqlParseLike;
- }
- else
- {
- tokenizerReuse(tkz);
- return p;
- }
- struct rqlParse *r = rqlParseUnaryMinus(tkz);
- AllocVar(p);
- p->op = op;
- p->type = rqlTypeBoolean;
-
- /* Now force children to be the same type, inserting casts if need be. */
- if (forceString)
- {
- if (l->type != rqlTypeString || r->type != rqlTypeString)
- {
- errAbort("Expecting string type around comparison line %d of %s",
- tkz->lf->lineIx, tkz->lf->fileName);
- }
- }
- else
- {
- enum rqlType childType = commonTypeForBop(l->type, r->type);
- l = rqlParseCoerce(l, childType);
- r = rqlParseCoerce(r, childType);
- }
-
- /* Now hang children onto node. */
- p->children = l;
- l->next = r;
- }
-return p;
-}
-
-struct rqlParse *rqlParseAndLoop(struct tokenizer *tkz)
-/* Parse out and or or. */
-{
-struct rqlParse *l = rqlParseCoerce(rqlParseCmp(tkz), rqlTypeBoolean);
-struct rqlParse *parent = NULL;
-struct rqlParse *p = l;
-for (;;)
- {
- char *tok = tokenizerNext(tkz);
- if (tok == NULL || !sameString(tok, "and"))
- {
- tokenizerReuse(tkz);
- return p;
- }
- else
- {
- if (parent == NULL)
- {
- AllocVar(parent);
- parent->op = rqlParseAnd;
- parent->type = rqlTypeBoolean;
- parent->children = p;
- p = parent;
- }
- struct rqlParse *r = rqlParseCoerce(rqlParseCmp(tkz), rqlTypeBoolean);
- slAddTail(&parent->children, r);
- }
- }
-}
-
-struct rqlParse *rqlParseOrLoop(struct tokenizer *tkz)
-/* Parse out and or or. */
-{
-struct rqlParse *l = rqlParseCoerce(rqlParseAndLoop(tkz), rqlTypeBoolean);
-struct rqlParse *parent = NULL;
-struct rqlParse *p = l;
-for (;;)
- {
- char *tok = tokenizerNext(tkz);
- if (tok == NULL || !sameString(tok, "or"))
- {
- tokenizerReuse(tkz);
- return p;
- }
- else
- {
- if (parent == NULL)
- {
- AllocVar(parent);
- parent->op = rqlParseOr;
- parent->type = rqlTypeBoolean;
- parent->children = p;
- p = parent;
- }
- struct rqlParse *r = rqlParseCoerce(rqlParseAndLoop(tkz), rqlTypeBoolean);
- slAddTail(&parent->children, r);
- }
- }
-}
-
-struct rqlParse *rqlParseExpression(struct tokenizer *tkz)
-/* Parse out a clause, usually a where clause. */
-{
-return rqlParseOrLoop(tkz);
-}
-
-char *rqlParseFieldSpec(struct tokenizer *tkz, struct dyString *buf)
-/* Return a field spec, which may contain * and ?. Put results in buf, and
- * return buf->string. */
-{
-boolean firstTime = TRUE;
-dyStringClear(buf);
-for (;;)
- {
- char *tok = tokenizerNext(tkz);
- if (tok == NULL)
- break;
- char c = *tok;
- if (c == '?' || c == '*' || isalpha(c) || c == '_')
- {
- if (firstTime)
- dyStringAppend(buf, tok);
- else
- {
- if (tkz->leadingSpaces == 0)
- dyStringAppend(buf, tok);
- else
- {
- tokenizerReuse(tkz);
- break;
- }
- }
- }
- else
- {
- tokenizerReuse(tkz);
- break;
- }
- firstTime = FALSE;
- }
-if (buf->stringSize == 0)
- errAbort("Expecting field name line %d of %s", tkz->lf->lineIx, tkz->lf->fileName);
-return buf->string;
-}
-
-void rqlStatementDump(struct rqlStatement *rql, FILE *f)
-/* Print out statement to file. */
-{
-fprintf(f, "%s", rql->command);
-if (rql->fieldList)
- {
- fprintf(f, " ");
- struct slName *field = rql->fieldList;
- fprintf(f, "%s", field->name);
- for (field = field->next; field != NULL; field = field->next)
- fprintf(f, ",%s", field->name);
- }
-if (rql->whereClause)
- {
- fprintf(f, " where:\n");
- rqlParseDump(rql->whereClause, 0, f);
- }
-fprintf(f, "\n");
-}
-
struct rqlEval rqlEvalOnRecord(struct rqlParse *p, struct raRecord *ra);
/* Evaluate self on ra. */
-struct rqlEval rqlEvalCoerceToBoolean(struct rqlEval r)
-/* Return TRUE if it's a nonempty string or a non-zero number. */
-{
-switch (r.type)
- {
- case rqlTypeBoolean:
- break; /* It's already done. */
- case rqlTypeString:
- r.val.b = (r.val.s != NULL && r.val.s[0] != 0);
- break;
- case rqlTypeInt:
- r.val.b = (r.val.i != 0);
- break;
- case rqlTypeDouble:
- r.val.b = (r.val.x != 0.0);
- break;
- default:
- internalErr();
- r.val.b = FALSE;
- break;
- }
-r.type = rqlTypeBoolean;
-return r;
-}
-
-struct raField *raRecordField(struct raRecord *ra, char *fieldName)
-/* Return named field if it exists, otherwise NULL */
+static void mergeRecords(struct raRecord *old, struct raRecord *record, struct lm *lm)
+/* Merge record into old, updating any old fields with new record values. */
{
struct raField *field;
-for (field = ra->fieldList; field != NULL; field = field->next)
+for (field = record->fieldList; field != NULL; field = field->next)
{
- if (sameString(field->name, fieldName))
- return field;
- }
-return NULL;
-}
-
-struct rqlEval rqlEvalEq(struct rqlParse *p, struct raRecord *ra)
-/* Return true if two children are equal, doing some casting if need be. */
-{
-struct rqlParse *lp = p->children;
-struct rqlParse *rp = lp->next;
-struct rqlEval lv = rqlEvalOnRecord(lp, ra);
-struct rqlEval rv = rqlEvalOnRecord(rp, ra);
-struct rqlEval res;
-res.type = rqlTypeBoolean;
-switch (lv.type)
- {
- case rqlTypeBoolean:
- res.val.b = (lv.val.b == rv.val.b);
- break;
- case rqlTypeString:
- res.val.b = sameString(lv.val.s, rv.val.s);
- break;
- case rqlTypeInt:
- res.val.b = (lv.val.i == rv.val.i);
- break;
- case rqlTypeDouble:
- res.val.b = (lv.val.x == rv.val.x);
- break;
- default:
- internalErr();
- res.val.b = FALSE;
- break;
- }
-return res;
-}
-
-struct rqlEval rqlEvalLt(struct rqlParse *p, struct raRecord *ra)
-/* Return true if r < l . */
-{
-struct rqlParse *lp = p->children;
-struct rqlParse *rp = lp->next;
-struct rqlEval lv = rqlEvalOnRecord(lp, ra);
-struct rqlEval rv = rqlEvalOnRecord(rp, ra);
-struct rqlEval res;
-res.type = rqlTypeBoolean;
-switch (lv.type)
+ struct raField *oldField = raRecordField(old, field->name);
+ if (oldField != NULL)
+ oldField->val = field->val;
+ else
{
- case rqlTypeBoolean:
- res.val.b = (lv.val.b < rv.val.b);
- break;
- case rqlTypeString:
- res.val.b = strcmp(lv.val.s, rv.val.s) < 0;
- break;
- case rqlTypeInt:
- res.val.b = (lv.val.i < rv.val.i);
- break;
- case rqlTypeDouble:
- res.val.b = (lv.val.x < rv.val.x);
- break;
- default:
- internalErr();
- res.val.b = FALSE;
- break;
+ lmAllocVar(lm, oldField);
+ oldField->name = field->name;
+ oldField->val = field->val;
+ slAddTail(&old->fieldList, oldField);
}
-return res;
-}
-
-struct rqlEval rqlEvalGt(struct rqlParse *p, struct raRecord *ra)
-/* Return true if r > l . */
-{
-struct rqlParse *lp = p->children;
-struct rqlParse *rp = lp->next;
-struct rqlEval lv = rqlEvalOnRecord(lp, ra);
-struct rqlEval rv = rqlEvalOnRecord(rp, ra);
-struct rqlEval res;
-res.type = rqlTypeBoolean;
-switch (lv.type)
- {
- case rqlTypeBoolean:
- res.val.b = (lv.val.b > rv.val.b);
- break;
- case rqlTypeString:
- res.val.b = strcmp(lv.val.s, rv.val.s) > 0;
- break;
- case rqlTypeInt:
- res.val.b = (lv.val.i > rv.val.i);
- break;
- case rqlTypeDouble:
- res.val.b = (lv.val.x > rv.val.x);
- break;
- default:
- internalErr();
- res.val.b = FALSE;
- break;
}
-return res;
}
-struct rqlEval rqlEvalLike(struct rqlParse *p, struct raRecord *ra)
-/* Return true if r like l . */
-{
-struct rqlParse *lp = p->children;
-struct rqlParse *rp = lp->next;
-struct rqlEval lv = rqlEvalOnRecord(lp, ra);
-struct rqlEval rv = rqlEvalOnRecord(rp, ra);
-struct rqlEval res;
-res.type = rqlTypeBoolean;
-assert(rv.type == rqlTypeString);
-assert(rv.type == lv.type);
-res.val.b = sqlMatchLike(rv.val.s, lv.val.s);
-return res;
-}
-
-struct rqlEval rqlEvalOnRecord(struct rqlParse *p, struct raRecord *ra)
-/* Evaluate self on ra. */
+static struct raRecord *readRaRecords(int inCount, char *inNames[], char *mergeField, struct lm *lm)
+/* Scan through files, merging records on mergeField if it is non-NULL. */
{
-struct rqlEval res;
-switch (p->op)
- {
- case rqlParseLiteral:
- res.val = p->val;
- res.type = p->type;
- break;
- case rqlParseSymbol:
- res.type = rqlTypeString;
- struct raField *f = raRecordField(ra, p->val.s);
- if (f == NULL)
- res.val.s = "";
- else
- res.val.s = f->val;
- break;
- case rqlParseEq:
- res = rqlEvalEq(p, ra);
- break;
- case rqlParseNe:
- res = rqlEvalEq(p, ra);
- res.val.b = !res.val.b;
- break;
-
- /* Inequalities. */
- case rqlParseLt:
- res = rqlEvalLt(p, ra);
- break;
- case rqlParseGt:
- res = rqlEvalGt(p, ra);
- break;
- case rqlParseLe:
- res = rqlEvalGt(p, ra);
- res.val.b = !res.val.b;
- break;
- case rqlParseGe:
- res = rqlEvalLt(p, ra);
- res.val.b = !res.val.b;
- break;
- case rqlParseLike:
- res = rqlEvalLike(p,ra);
- break;
-
- /* Logical ops. */
- case rqlParseAnd:
- {
- res.type = rqlTypeBoolean;
- res.val.b = TRUE;
- struct rqlParse *c;
- for (c = p->children; c != NULL; c= c->next)
+if (inCount <= 0)
+ return NULL;
+if (mergeField)
{
- struct rqlEval e = rqlEvalOnRecord(c, ra);
- if (!e.val.b)
+ struct raRecord *recordList = NULL, *record;
+ struct hash *recordHash = hashNew(0);
+ slReverse(&recordList);
+ int i;
+ for (i=0; i<inCount; ++i)
{
- res.val.b = FALSE;
- break;
- }
- }
- break;
- }
- case rqlParseOr:
+ char *fileName = inNames[i];
+ struct lineFile *lf = lineFileOpen(fileName, TRUE);
+ while ((record = raRecordReadOne(lf, lm)) != NULL)
{
- res.type = rqlTypeBoolean;
- res.val.b = FALSE;
- struct rqlParse *c;
- for (c = p->children; c != NULL; c= c->next)
+ struct raField *keyField = raRecordField(record, mergeField);
+ if (keyField != NULL)
{
- struct rqlEval e = rqlEvalOnRecord(c, ra);
- if (e.val.b)
+ struct raRecord *oldRecord = hashFindVal(recordHash, keyField->val);
+ if (oldRecord != NULL)
{
- res.val.b = TRUE;
- break;
+ mergeRecords(oldRecord, record, lm);
}
- }
- break;
- }
-
- /* Type casts. */
- case rqlParseStringToBoolean:
- res = rqlEvalOnRecord(p->children, ra);
- res.type = rqlTypeBoolean;
- res.val.b = (res.val.s[0] != 0);
- break;
- case rqlParseIntToBoolean:
- res = rqlEvalOnRecord(p->children, ra);
- res.type = rqlTypeBoolean;
- res.val.b = (res.val.i != 0);
- break;
- case rqlParseDoubleToBoolean:
- res = rqlEvalOnRecord(p->children, ra);
- res.type = rqlTypeBoolean;
- res.val.b = (res.val.x != 0.0);
- break;
- case rqlParseStringToInt:
- res = rqlEvalOnRecord(p->children, ra);
- res.type = rqlTypeInt;
- res.val.i = atoi(res.val.s);
- break;
- case rqlParseStringToDouble:
- res = rqlEvalOnRecord(p->children, ra);
- res.type = rqlTypeDouble;
- res.val.x = atof(res.val.s);
- break;
- case rqlParseBooleanToInt:
- res = rqlEvalOnRecord(p->children, ra);
- res.type = rqlTypeInt;
- res.val.i = res.val.b;
- break;
- case rqlParseBooleanToDouble:
- res = rqlEvalOnRecord(p->children, ra);
- res.type = rqlTypeDouble;
- res.val.x = res.val.b;
- break;
- case rqlParseIntToDouble:
- res = rqlEvalOnRecord(p->children, ra);
- res.type = rqlTypeDouble;
- res.val.x = res.val.b;
- break;
-
- case rqlParseUnaryMinusDouble:
- res = rqlEvalOnRecord(p->children, ra);
- res.type = rqlTypeDouble;
- res.val.x = -res.val.x;
- break;
-
- default:
- errAbort("Unknown op %s\n", rqlParseOpToString(p->op));
- res.type = rqlTypeInt; // Keep compiler from complaining.
- res.val.i = 0; // Keep compiler from complaining.
- break;
- }
-return res;
-}
-
-struct rqlStatement *rqlStatementParse(struct lineFile *lf)
-/* Parse an RQL statement out of text */
-{
-struct tokenizer *tkz = tokenizerOnLineFile(lf);
-tkz->uncommentShell = TRUE;
-tkz->uncommentC = TRUE;
-tkz->leaveQuotes = TRUE;
-struct rqlStatement *rql;
-AllocVar(rql);
-rql->command = cloneString(tokenizerMustHaveNext(tkz));
-if (sameString(rql->command, "select"))
- {
- struct dyString *buf = dyStringNew(0);
- struct slName *list = NULL;
- char *tok = rqlParseFieldSpec(tkz, buf);
- list = slNameNew(tok);
- for (;;)
- {
- /* Parse out comma-separated field list. */
- char *comma = tokenizerNext(tkz);
- if (comma == NULL || comma[0] != ',')
+ else
{
- tokenizerReuse(tkz);
- break;
- }
- struct slName *field = slNameAddHead(&list, rqlParseFieldSpec(tkz, buf));
+ record->key = keyField;
+ slAddHead(&recordList, record);
+ hashAdd(recordHash, keyField->val, record);
}
- slReverse(&list);
- rql->fieldList = list;
- dyStringFree(&buf);
}
-else if (sameString(rql->command, "count"))
- {
- /* No parameters to count. */
}
-else
- errAbort("Unknown RQL command '%s line %d of %s\n", rql->command, lf->lineIx, lf->fileName);
-
-char *where = tokenizerNext(tkz);
-if (where != NULL)
- {
- if (!sameString(where, "where"))
- errAbort("Unknown clause '%s' line %d of %s", where, lf->lineIx, lf->fileName);
- rql->whereClause = rqlParseExpression(tkz);
- }
-
-char *extra = tokenizerNext(tkz);
-if (extra != NULL)
- errAbort("Extra stuff starting with '%s' past end of statement line %d of %s",
- extra, lf->lineIx, lf->fileName);
-return rql;
-}
-
-struct raField *raFieldFromLine(char *line, struct lm *lm)
-/* Parse out line and convert it to a raField. Will return NULL on empty lines.
- * Will insert some zeroes into the input line as well. */
-{
-char *word = nextWord(&line);
-if (word == NULL)
- return NULL;
-struct raField *field;
-lmAllocVar(lm, field);
-field->name = lmCloneString(lm, word);
-char *val = emptyForNull(skipLeadingSpaces(line));
-field->val = lmCloneString(lm, val);
-return field;
-}
-
-struct raRecord *raRecordReadOne(struct lineFile *lf, struct lm *lm)
-/* Read next record from file. Returns NULL at end of file. */
-{
-struct raField *field, *fieldList = NULL;
-char *line, *word;
-
-/* Read first line and start fieldList on it. */
-if (!lineFileNextReal(lf, &line))
- return NULL;
-fieldList = raFieldFromLine(line, lm);
-
-/* Keep going until get a blank line. */
-for (;;)
- {
- if (!lineFileNext(lf, &line, NULL))
- break;
- field = raFieldFromLine(line, lm);
- if (field == NULL)
- break;
- slAddHead(&fieldList, field);
+ lineFileClose(&lf);
}
-
-slReverse(&fieldList);
-struct raRecord *record;
-lmAllocVar(lm, record);
-record->fieldList = fieldList;
-return record;
-}
-
-static struct raRecord *readRaRecords(int inCount, char *inNames[], char *mergeField, struct lm *lm)
-/* Scan through files, merging records on mergeField if it is non-NULL. */
-{
-if (inCount <= 0)
- return NULL;
-if (mergeField)
- {
- errAbort("mergeField not yet supported");
- return NULL;
+ slReverse(&recordList);
+ return recordList;
}
else
{
struct raRecord *recordList = NULL;