src/utils/raSqlQuery/raSqlQuery.c 1.2
1.2 2009/11/20 01:18:09 kent
Adding casts to parse tree and evaluator so can have numeric as well as string types. Making floating point numbers parse.
Index: src/utils/raSqlQuery/raSqlQuery.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/utils/raSqlQuery/raSqlQuery.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -b -B -U 4 -r1.1 -r1.2
--- src/utils/raSqlQuery/raSqlQuery.c 20 Nov 2009 00:24:55 -0000 1.1
+++ src/utils/raSqlQuery/raSqlQuery.c 20 Nov 2009 01:18:09 -0000 1.2
@@ -72,8 +72,16 @@
rqlParseLiteral, /* Literal string or number. */
rqlParseSymbol, /* A symbol name. */
rqlParseEq, /* An equals comparison */
rqlParseNe, /* A not equals comparison */
+
+ rqlParseStringToBoolean,
+ rqlParseStringToInt,
+ rqlParseStringToDouble,
+ rqlParseBooleanToInt,
+ rqlParseBooleanToDouble,
+ rqlParseIntToDouble,
+
rqlParseAnd, /* An and */
rqlParseOr, /* An or */
};
@@ -93,8 +101,22 @@
case rqlParseAnd:
return "rqlParseAnd";
case rqlParseOr:
return "rqlParseOr";
+
+ case rqlParseStringToBoolean:
+ return "rqlParseStringToBoolean";
+ case rqlParseStringToInt:
+ return "rqlParseStringToInt";
+ case rqlParseStringToDouble:
+ return "rqlParseStringToDouble";
+ case rqlParseBooleanToInt:
+ return "rqlParseBooleanToInt";
+ case rqlParseBooleanToDouble:
+ return "rqlParseBooleanToDouble";
+ case rqlParseIntToDouble:
+ return "rqlParseIntToDouble";
+
default:
return "rqlParseUnknown";
}
}
@@ -197,17 +219,22 @@
}
else if (isdigit(c))
{
p->op = rqlParseLiteral;
- if (strchr(tok, '.'))
+ 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(tok);
+ p->val.x = sqlDouble(buf);
}
else
- {
- p->type = rqlTypeInt;
- p->val.i = sqlUnsigned(tok);
+ tokenizerReuse(tkz);
}
}
else
{
@@ -230,8 +257,104 @@
if (!sameString(tkz->string, expecting))
expectingGot(tkz, expecting, tkz->string);
}
+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;
+ 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 *rqlParseCmp(struct tokenizer *tkz)
/* Parse out comparison. */
{
struct rqlParse *l = rqlParseAtom(tkz);
@@ -257,8 +380,15 @@
struct rqlParse *r = rqlParseAtom(tkz);
AllocVar(p);
p->op = op;
p->type = rqlTypeBoolean;
+
+ /* Now force children to be the same type, inserting casts if need be. */
+ 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;
@@ -309,66 +439,16 @@
case rqlTypeDouble:
r.val.b = (r.val.x != 0.0);
break;
default:
- errAbort("Unknown type %d", r.type);
+ internalErr();
r.val.b = FALSE;
break;
}
r.type = rqlTypeBoolean;
return r;
}
-struct rqlEval rqlEvalCoerceToInt(struct rqlEval r)
-/* Return TRUE if it's a nonempty string or a non-zero number. */
-{
-switch (r.type)
- {
- case rqlTypeBoolean:
- r.val.i = r.val.b;
- break;
- case rqlTypeInt:
- break; /* It's already done. */
- case rqlTypeDouble:
- r.val.i = r.val.x;
- break;
- case rqlTypeString:
- r.val.i = atoi(r.val.s);
- break;
- default:
- errAbort("Unknown type %d", r.type);
- r.val.i = 0;
- break;
- }
-r.type = rqlTypeInt;
-return r;
-}
-
-struct rqlEval rqlEvalCoerceToFloat(struct rqlEval r)
-/* Convert to floating point. */
-{
-switch (r.type)
- {
- case rqlTypeBoolean:
- r.val.x = r.val.b;
- break;
- case rqlTypeInt:
- r.val.x = r.val.i;
- break;
- case rqlTypeDouble:
- break; /* It's already done. */
- case rqlTypeString:
- r.val.x = atof(r.val.s);
- break;
- default:
- errAbort("Unknown type %d", r.type);
- r.val.x = 0;
- break;
- }
-r.type = rqlTypeDouble;
-return r;
-}
-
struct raField *raRecordField(struct raRecord *ra, char *fieldName)
/* Return named field if it exists, otherwise NULL */
{
struct raField *field;
@@ -436,8 +516,41 @@
case rqlParseNe:
res = rqlEvalEq(p, ra);
res.val.b = !res.val.b;
break;
+
+ /* Type casts. */
+ case rqlParseStringToBoolean:
+ res = rqlEvalOnRecord(p->children, ra);
+ res.type = rqlTypeBoolean;
+ res.val.b = (res.val.s[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;
+
default:
errAbort("Unknown op %s\n", rqlParseOpToString(p->op));
res.type = rqlTypeInt; // Keep compiler from complaining.
res.val.i = 0; // Keep compiler from complaining.