use of com.questdb.griffin.common.ExprNode in project questdb by bluestreak01.
the class QueryModel method toSink0.
private void toSink0(CharSink sink, boolean joinSlave) {
if (columns.size() > 0) {
sink.put(getSelectModelTypeText()).put(' ');
for (int i = 0, n = columns.size(); i < n; i++) {
if (i > 0) {
sink.put(", ");
}
QueryColumn column = columns.getQuick(i);
CharSequence name = column.getName();
CharSequence alias = column.getAlias();
ExprNode ast = column.getAst();
if (column instanceof AnalyticColumn || name == null) {
ast.toSink(sink);
if (alias != null) {
aliasToSink(alias, sink);
}
// this can only be analytic column
if (name != null) {
AnalyticColumn ac = (AnalyticColumn) column;
sink.put(" over (");
final ObjList<ExprNode> partitionBy = ac.getPartitionBy();
if (partitionBy.size() > 0) {
sink.put("partition by ");
for (int k = 0, z = partitionBy.size(); k < z; k++) {
if (k > 0) {
sink.put(", ");
}
partitionBy.getQuick(k).toSink(sink);
}
}
final ObjList<ExprNode> orderBy = ac.getOrderBy();
if (orderBy.size() > 0) {
if (partitionBy.size() > 0) {
sink.put(' ');
}
sink.put("order by ");
for (int k = 0, z = orderBy.size(); k < z; k++) {
if (k > 0) {
sink.put(", ");
}
orderBy.getQuick(k).toSink(sink);
if (ac.getOrderByDirection().getQuick(k) == 1) {
sink.put(" desc");
}
}
}
sink.put(')');
}
} else {
ast.toSink(sink);
// do not repeat alias when it is the same as AST token, provided AST is a literal
if (alias != null && (ast.type != ExprNode.LITERAL || !ast.token.equals(alias))) {
aliasToSink(alias, sink);
}
}
}
sink.put(" from ");
}
if (tableName != null) {
sink.put(tableName.token);
if (alias != null) {
aliasToSink(alias.token, sink);
}
} else {
sink.put('(');
nestedModel.toSink(sink);
sink.put(')');
if (alias != null) {
aliasToSink(alias.token, sink);
}
}
if (timestamp != null) {
sink.put(" timestamp (");
timestamp.toSink(sink);
sink.put(')');
}
if (getLatestBy() != null) {
sink.put(" latest by ");
getLatestBy().toSink(sink);
}
if (orderedJoinModels.size() > 1) {
for (int i = 0, n = orderedJoinModels.size(); i < n; i++) {
QueryModel model = joinModels.getQuick(orderedJoinModels.getQuick(i));
if (model != this) {
switch(model.getJoinType()) {
case JOIN_OUTER:
sink.put(" outer join ");
break;
case JOIN_ASOF:
sink.put(" asof join ");
break;
case JOIN_CROSS:
sink.put(" cross join ");
break;
default:
sink.put(" join ");
}
if (model.getWhereClause() != null) {
sink.put('(');
model.toSink0(sink, true);
sink.put(')');
if (model.getAlias() != null) {
aliasToSink(model.getAlias().token, sink);
} else if (model.getTableName() != null) {
aliasToSink(model.getTableName().token, sink);
}
} else {
model.toSink0(sink, true);
}
JoinContext jc = model.getContext();
if (jc != null && jc.aIndexes.size() > 0) {
// join clause
sink.put(" on ");
for (int k = 0, z = jc.aIndexes.size(); k < z; k++) {
if (k > 0) {
sink.put(" and ");
}
jc.aNodes.getQuick(k).toSink(sink);
sink.put(" = ");
jc.bNodes.getQuick(k).toSink(sink);
}
}
if (model.getPostJoinWhereClause() != null) {
sink.put(" post-join-where ");
model.getPostJoinWhereClause().toSink(sink);
}
}
}
}
if (whereClause != null) {
sink.put(" where ");
whereClause.toSink(sink);
}
if (constWhereClause != null) {
sink.put(" const-where ");
constWhereClause.toSink(sink);
}
if (!joinSlave && postJoinWhereClause != null) {
sink.put(" post-join-where ");
postJoinWhereClause.toSink(sink);
}
if (sampleBy != null) {
sink.put(" sample by ");
sampleBy.toSink(sink);
}
if (orderBy.size() > 0) {
sink.put(" order by ");
for (int i = 0, n = orderBy.size(); i < n; i++) {
if (i > 0) {
sink.put(", ");
}
orderBy.getQuick(i).toSink(sink);
if (orderByDirection.getQuick(i) == 1) {
sink.put(" desc");
}
}
}
if (getLimitLo() != null || getLimitHi() != null) {
sink.put(" limit ");
if (getLimitLo() != null) {
getLimitLo().toSink(sink);
}
if (getLimitHi() != null) {
sink.put(',');
getLimitHi().toSink(sink);
}
}
}
use of com.questdb.griffin.common.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 ParserException.$(node.position, "Too few arguments for 'in'");
}
ExprNode col = node.paramCount < 3 ? node.lhs : node.args.getLast();
if (col.type != ExprNode.LITERAL) {
throw ParserException.$(col.position, "Column name expected");
}
CharSequence column = translator.translateAlias(col.token);
if (metadata.getColumnIndexQuiet(column) == -1) {
throw ParserException.invalidColumn(col.position, col.token);
}
return analyzeInInterval(model, col, node) || analyzeListOfValues(model, column, metadata, node) || analyzeInLambda(model, column, metadata, node);
}
use of com.questdb.griffin.common.ExprNode in project questdb by bluestreak01.
the class QueryFilterAnalyser method analyzeInInterval.
private boolean analyzeInInterval(IntrinsicModel model, ExprNode col, ExprNode in) throws ParserException {
if (!isTimestamp(col)) {
return false;
}
if (in.paramCount > 3) {
throw ParserException.$(in.args.getQuick(0).position, "Too many args");
}
if (in.paramCount < 3) {
throw ParserException.$(in.position, "Too few args");
}
ExprNode lo = in.args.getQuick(1);
ExprNode hi = in.args.getQuick(0);
if (lo.type == ExprNode.CONSTANT && hi.type == ExprNode.CONSTANT) {
long loMillis;
long hiMillis;
try {
loMillis = DateFormatUtils.tryParse(lo.token, 1, lo.token.length() - 1);
} catch (NumericException ignore) {
throw ParserException.invalidDate(lo.position);
}
try {
hiMillis = DateFormatUtils.tryParse(hi.token, 1, hi.token.length() - 1);
} catch (NumericException ignore) {
throw ParserException.invalidDate(hi.position);
}
model.intersectIntervals(loMillis, hiMillis);
in.intrinsicValue = IntrinsicValue.TRUE;
return true;
}
return false;
}
use of com.questdb.griffin.common.ExprNode in project questdb by bluestreak01.
the class ExprParser method parseExpr.
@SuppressWarnings("ConstantConditions")
public void parseExpr(Lexer2 lexer, ExprListener listener) throws ParserException {
opStack.clear();
paramCountStack.clear();
int paramCount = 0;
int braceCount = 0;
int caseCount = 0;
ExprNode node;
CharSequence tok;
char thisChar;
int prevBranch;
int thisBranch = BRANCH_NONE;
OUT: while ((tok = lexer.optionTok()) != null) {
thisChar = tok.charAt(0);
prevBranch = thisBranch;
switch(thisChar) {
case ',':
if (prevBranch == BRANCH_COMMA || prevBranch == BRANCH_LEFT_BRACE) {
throw missingArgs(lexer.position());
}
thisBranch = BRANCH_COMMA;
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 (prevBranch == BRANCH_COMMA) {
throw missingArgs(lexer.position());
}
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 = (prevBranch == BRANCH_LEFT_BRACE ? 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, lexer.toImmutable(tok), 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, lexer.toImmutable(tok), 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 (caseCount > 0 || nonLiteralBranches.excludes(thisBranch)) {
thisBranch = BRANCH_LITERAL;
// are going to flush operation stack
if (thisChar == 'c' && Chars.equals("case", tok)) {
caseCount++;
paramCountStack.push(paramCount);
paramCount = 0;
opStack.push(exprNodePool.next().of(ExprNode.FUNCTION, lexer.toImmutable(tok), Integer.MAX_VALUE, lexer.position()));
continue;
}
if (caseCount > 0) {
switch(thisChar) {
case 'e':
if (Chars.equals("end", tok)) {
if (prevBranch == BRANCH_CASE_CONTROL) {
throw missingArgs(lexer.position());
}
// If the stack runs out without finding a left parenthesis, then there are mismatched parentheses.
while ((node = opStack.poll()) != null && !Chars.equals("case", node.token)) {
listener.onNode(node);
}
node.paramCount = paramCount;
listener.onNode(node);
// make sure we restore paramCount
if (paramCountStack.notEmpty()) {
paramCount = paramCountStack.pop();
}
caseCount--;
continue;
}
// fall through
case 'w':
case 't':
int keywordIndex = caseKeywords.get(tok);
if (keywordIndex > -1) {
if (prevBranch == BRANCH_CASE_CONTROL) {
throw missingArgs(lexer.position());
}
switch(keywordIndex) {
// when
case 0:
case // else
2:
if ((paramCount % 2) != 0) {
throw ParserException.$(lexer.position(), "'then' expected");
}
break;
default:
// then
if ((paramCount % 2) == 0) {
throw ParserException.$(lexer.position(), "'when' expected");
}
break;
}
while ((node = opStack.poll()) != null && !Chars.equals("case", node.token)) {
listener.onNode(node);
}
if (node != null) {
opStack.push(node);
}
paramCount++;
thisBranch = BRANCH_CASE_CONTROL;
continue;
}
break;
}
}
// If the token is a function token, then push it onto the stack.
opStack.push(exprNodePool.next().of(ExprNode.LITERAL, lexer.toImmutable(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 ParserException.$(node.position, "unbalanced (");
}
if (Chars.equals("case", node.token)) {
throw ParserException.$(node.position, "unbalanced 'case'");
}
listener.onNode(node);
}
}
use of com.questdb.griffin.common.ExprNode in project questdb by bluestreak01.
the class SqlLexerOptimiser method addTransitiveFilters.
/**
* Adds filters derived from transitivity of equals operation, for example
* if there is filter:
* <p>
* a.x = b.x and b.x = 10
* <p>
* derived filter would be:
* <p>
* a.x = 10
* <p>
* this filter is not explicitly mentioned but it might help pre-filtering record sources
* before hashing.
*/
private void addTransitiveFilters(QueryModel parent) {
ObjList<QueryModel> joinModels = parent.getJoinModels();
for (int i = 0, n = joinModels.size(); i < n; i++) {
JoinContext jc = joinModels.getQuick(i).getContext();
if (jc != null) {
for (int k = 0, kn = jc.bNames.size(); k < kn; k++) {
CharSequence name = jc.bNames.getQuick(k);
if (constNameToIndex.get(name) == jc.bIndexes.getQuick(k)) {
ExprNode node = exprNodePool.next().of(ExprNode.OPERATION, constNameToToken.get(name), 0, 0);
node.lhs = jc.aNodes.getQuick(k);
node.rhs = constNameToNode.get(name);
node.paramCount = 2;
addWhereNode(parent, jc.slaveIndex, node);
}
}
}
}
}
Aggregations