use of com.questdb.parser.sql.model.ExprNode in project questdb by bluestreak01.
the class InvertedBooleanOptimisationTest method assertOk.
private void assertOk(CharSequence expected, String expression) throws ParserException {
lexer.setContent(expression);
p.parseExpr(lexer, ast);
ExprNode n = compiler.optimiseInvertedBooleans(ast.poll(), false);
Assert.assertNotNull(n);
TestUtils.assertEquals(expected, toRpn(n));
}
use of com.questdb.parser.sql.model.ExprNode in project questdb by bluestreak01.
the class ExprParser method parseExpr.
@SuppressWarnings("ConstantConditions")
public void parseExpr(Lexer lexer, ExprListener listener) throws ParserException {
opStack.clear();
paramCountStack.clear();
int paramCount = 0;
int braceCount = 0;
ExprNode node;
CharSequence tok;
char thisChar = 0, prevChar;
int prevBranch;
int thisBranch = BRANCH_NONE;
OUT: while ((tok = lexer.optionTok()) != null) {
prevChar = thisChar;
thisChar = tok.charAt(0);
prevBranch = thisBranch;
switch(thisChar) {
case ',':
thisBranch = BRANCH_COMMA;
if (prevChar == ',') {
throw QueryError.$(lexer.position(), "Missing argument");
}
if (braceCount == 0) {
// comma outside of braces
lexer.unparse();
break OUT;
}
// parentheses were mismatched.
while ((node = opStack.poll()) != null && node.token.charAt(0) != '(') {
listener.onNode(node);
}
if (node != null) {
opStack.push(node);
}
paramCount++;
break;
case '(':
thisBranch = BRANCH_LEFT_BRACE;
braceCount++;
// If the token is a left parenthesis, then push it onto the stack.
paramCountStack.push(paramCount);
paramCount = 0;
opStack.push(exprNodePool.next().of(ExprNode.CONTROL, "(", Integer.MAX_VALUE, lexer.position()));
break;
case ')':
if (prevChar == ',') {
throw QueryError.$(lexer.position(), "Missing argument");
}
if (braceCount == 0) {
lexer.unparse();
break OUT;
}
thisBranch = BRANCH_RIGHT_BRACE;
braceCount--;
// If the stack runs out without finding a left parenthesis, then there are mismatched parentheses.
while ((node = opStack.poll()) != null && node.token.charAt(0) != '(') {
listener.onNode(node);
}
// enable operation or literal absorb parameters
if ((node = opStack.peek()) != null && (node.type == ExprNode.LITERAL || (node.type == ExprNode.SET_OPERATION))) {
node.paramCount = (prevChar == '(' ? 0 : paramCount + 1) + (node.paramCount == 2 ? 1 : 0);
node.type = ExprNode.FUNCTION;
listener.onNode(node);
opStack.poll();
if (paramCountStack.notEmpty()) {
paramCount = paramCountStack.pop();
}
}
break;
case '`':
thisBranch = BRANCH_LAMBDA;
// If the token is a number, then add it to the output queue.
listener.onNode(exprNodePool.next().of(ExprNode.LAMBDA, tok.toString(), 0, lexer.position()));
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '"':
case '\'':
case 'N':
case 'n':
if ((thisChar != 'N' && thisChar != 'n') || Chars.equals("NaN", tok) || Chars.equals("null", tok)) {
thisBranch = BRANCH_CONSTANT;
// If the token is a number, then add it to the output queue.
listener.onNode(exprNodePool.next().of(ExprNode.CONSTANT, tok.toString(), 0, lexer.position()));
break;
}
default:
ExprOperator op;
if ((op = ExprOperator.opMap.get(tok)) != null) {
thisBranch = BRANCH_OPERATOR;
// If the token is an operator, o1, then:
// while there is an operator token, o2, at the top of the operator stack, and either
// o1 is left-associative and its precedence is less than or equal to that of o2, or
// o1 is right associative, and has precedence less than that of o2,
// then pop o2 off the operator stack, onto the output queue;
// push o1 onto the operator stack.
int operatorType = op.type;
switch(thisChar) {
case '-':
switch(prevBranch) {
case BRANCH_OPERATOR:
case BRANCH_LEFT_BRACE:
case BRANCH_COMMA:
case BRANCH_NONE:
// we have unary minus
operatorType = ExprOperator.UNARY;
break;
default:
break;
}
break;
default:
break;
}
ExprNode other;
// this is to maintain correctness of -a^b
while ((other = opStack.peek()) != null) {
boolean greaterPrecedence = (op.leftAssociative && op.precedence >= other.precedence) || (!op.leftAssociative && op.precedence > other.precedence);
if (greaterPrecedence && (operatorType != ExprOperator.UNARY || (operatorType == ExprOperator.UNARY && other.paramCount == 1))) {
listener.onNode(other);
opStack.poll();
} else {
break;
}
}
node = exprNodePool.next().of(op.type == ExprOperator.SET ? ExprNode.SET_OPERATION : ExprNode.OPERATION, op.token, op.precedence, lexer.position());
switch(operatorType) {
case ExprOperator.UNARY:
node.paramCount = 1;
break;
default:
node.paramCount = 2;
break;
}
opStack.push(node);
} else if (!nonLiteralBranches.contains(thisBranch)) {
thisBranch = BRANCH_LITERAL;
// If the token is a function token, then push it onto the stack.
opStack.push(exprNodePool.next().of(ExprNode.LITERAL, Chars.toString(tok), Integer.MIN_VALUE, lexer.position()));
} else {
// literal can be at start of input, after a bracket or part of an operator
// all other cases are illegal and will be considered end-of-input
lexer.unparse();
break OUT;
}
}
}
while ((node = opStack.poll()) != null) {
if (node.token.charAt(0) == '(') {
throw QueryError.$(node.position, "Unbalanced (");
}
listener.onNode(node);
}
}
use of com.questdb.parser.sql.model.ExprNode in project questdb by bluestreak01.
the class QueryFilterAnalyser method analyzeNotIn.
private boolean analyzeNotIn(AliasTranslator translator, IntrinsicModel model, ExprNode notNode, RecordMetadata m) throws ParserException {
ExprNode node = notNode.rhs;
if (node.paramCount < 2) {
throw QueryError.$(node.position, "Too few arguments for 'in'");
}
ExprNode col = node.paramCount < 3 ? node.lhs : node.args.getLast();
if (col.type != ExprNode.LITERAL) {
throw QueryError.$(col.position, "Column name expected");
}
String column = translator.translateAlias(col.token).toString();
if (m.getColumnIndexQuiet(column) == -1) {
throw QueryError.invalidColumn(col.position, col.token);
}
boolean ok = analyzeNotInInterval(model, col, node);
if (ok) {
notNode.intrinsicValue = IntrinsicValue.TRUE;
} else {
analyzeNotListOfValues(column, m, notNode);
}
return ok;
}
use of com.questdb.parser.sql.model.ExprNode in project questdb by bluestreak01.
the class QueryFilterAnalyser method analyzeListOfValues.
private boolean analyzeListOfValues(IntrinsicModel model, String col, RecordMetadata meta, ExprNode node) {
RecordColumnMetadata colMeta = meta.getColumn(col);
if (colMeta.isIndexed()) {
boolean newColumn = true;
if (preferredKeyColumn != null && !col.equals(preferredKeyColumn)) {
return false;
}
// check if we already have indexed column and it is of worse selectivity
if (model.keyColumn != null && (newColumn = !model.keyColumn.equals(col)) && colMeta.getBucketCount() <= meta.getColumn(model.keyColumn).getBucketCount()) {
return false;
}
int i = node.paramCount - 1;
tempKeys.clear();
tempPos.clear();
// if any of values is not an indexed constant - bail out
if (i == 1) {
if (node.rhs == null || node.rhs.type != ExprNode.CONSTANT) {
return false;
}
if (tempKeys.add(Chars.stripQuotes(node.rhs.token))) {
tempPos.add(node.position);
}
} else {
for (i--; i > -1; i--) {
ExprNode c = node.args.getQuick(i);
if (c.type != ExprNode.CONSTANT) {
return false;
}
if (tempKeys.add(Chars.stripQuotes(c.token))) {
tempPos.add(c.position);
}
}
}
// and reset intrinsic values on nodes associated with old column
if (newColumn) {
model.keyValues.clear();
model.keyValuePositions.clear();
model.keyValues.addAll(tempKeys);
model.keyValuePositions.addAll(tempPos);
for (int n = 0, k = keyNodes.size(); n < k; n++) {
keyNodes.getQuick(n).intrinsicValue = IntrinsicValue.UNDEFINED;
}
keyNodes.clear();
model.keyColumn = col;
keyNodes.add(node);
node.intrinsicValue = IntrinsicValue.TRUE;
return true;
} else if (!model.keyValuesIsLambda) {
// calculate overlap of values
replaceAllWithOverlap(model);
keyNodes.add(node);
node.intrinsicValue = IntrinsicValue.TRUE;
return true;
}
}
return false;
}
use of com.questdb.parser.sql.model.ExprNode in project questdb by bluestreak01.
the class QueryFilterAnalyser method analyzeIn.
private boolean analyzeIn(AliasTranslator translator, IntrinsicModel model, ExprNode node, RecordMetadata metadata) throws ParserException {
if (node.paramCount < 2) {
throw QueryError.$(node.position, "Too few arguments for 'in'");
}
ExprNode col = node.paramCount < 3 ? node.lhs : node.args.getLast();
if (col.type != ExprNode.LITERAL) {
throw QueryError.$(col.position, "Column name expected");
}
String column = translator.translateAlias(col.token).toString();
if (metadata.getColumnIndexQuiet(column) == -1) {
throw QueryError.invalidColumn(col.position, col.token);
}
return analyzeInInterval(model, col, node) || analyzeListOfValues(model, column, metadata, node) || analyzeInLambda(model, column, metadata, node);
}
Aggregations