/* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.siddhi.extension.table.rdbms; import org.wso2.siddhi.core.table.record.BaseConditionVisitor; import org.wso2.siddhi.extension.table.rdbms.exception.RDBMSTableException; import org.wso2.siddhi.extension.table.rdbms.util.Constant; import org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants; import org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableUtils; import org.wso2.siddhi.query.api.definition.Attribute; import org.wso2.siddhi.query.api.expression.condition.Compare; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_AND; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_COMPARE_EQUAL; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_COMPARE_GREATER_THAN; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_COMPARE_GREATER_THAN_EQUAL; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_COMPARE_LESS_THAN; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_COMPARE_LESS_THAN_EQUAL; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_COMPARE_NOT_EQUAL; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_IN; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_IS_NULL; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_MATH_ADD; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_MATH_DIVIDE; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_MATH_MOD; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_MATH_MULTIPLY; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_MATH_SUBTRACT; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_NOT; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.SQL_OR; import static org.wso2.siddhi.extension.table.rdbms.util.RDBMSTableConstants.WHITESPACE; /** * Class which is used by the Siddhi runtime for instructions on converting the SiddhiQL condition to the condition * format understood by the underlying RDBMS data store. */ public class RDBMSConditionVisitor extends BaseConditionVisitor { private StringBuilder condition; private String finalCompiledCondition; private String tableName; private Map<String, Object> placeholders; private SortedMap<Integer, Object> parameters; private int streamVarCount; private int constantCount; public RDBMSConditionVisitor(String tableName) { this.tableName = tableName; this.condition = new StringBuilder(); this.streamVarCount = 0; this.constantCount = 0; this.placeholders = new HashMap<>(); this.parameters = new TreeMap<>(); } private RDBMSConditionVisitor() { //preventing initialization } public String returnCondition() { this.parametrizeCondition(); return this.finalCompiledCondition.trim(); } public SortedMap<Integer, Object> getParameters() { return this.parameters; } @Override public void beginVisitAnd() { condition.append(RDBMSTableConstants.OPEN_PARENTHESIS); } @Override public void endVisitAnd() { condition.append(RDBMSTableConstants.CLOSE_PARENTHESIS); } @Override public void beginVisitAndLeftOperand() { //Not applicable } @Override public void endVisitAndLeftOperand() { //Not applicable } @Override public void beginVisitAndRightOperand() { condition.append(SQL_AND).append(WHITESPACE); } @Override public void endVisitAndRightOperand() { //Not applicable } @Override public void beginVisitOr() { condition.append(RDBMSTableConstants.OPEN_PARENTHESIS); } @Override public void endVisitOr() { condition.append(RDBMSTableConstants.CLOSE_PARENTHESIS); } @Override public void beginVisitOrLeftOperand() { //Not applicable } @Override public void endVisitOrLeftOperand() { //Not applicable } @Override public void beginVisitOrRightOperand() { condition.append(SQL_OR).append(WHITESPACE); } @Override public void endVisitOrRightOperand() { //Not applicable } @Override public void beginVisitNot() { condition.append(SQL_NOT).append(WHITESPACE); } @Override public void endVisitNot() { //Not applicable } @Override public void beginVisitCompare(Compare.Operator operator) { condition.append(RDBMSTableConstants.OPEN_PARENTHESIS); } @Override public void endVisitCompare(Compare.Operator operator) { condition.append(RDBMSTableConstants.CLOSE_PARENTHESIS); } @Override public void beginVisitCompareLeftOperand(Compare.Operator operator) { //Not applicable } @Override public void endVisitCompareLeftOperand(Compare.Operator operator) { //Not applicable } @Override public void beginVisitCompareRightOperand(Compare.Operator operator) { switch (operator) { case EQUAL: condition.append(SQL_COMPARE_EQUAL); break; case GREATER_THAN: condition.append(SQL_COMPARE_GREATER_THAN); break; case GREATER_THAN_EQUAL: condition.append(SQL_COMPARE_GREATER_THAN_EQUAL); break; case LESS_THAN: condition.append(SQL_COMPARE_LESS_THAN); break; case LESS_THAN_EQUAL: condition.append(SQL_COMPARE_LESS_THAN_EQUAL); break; case NOT_EQUAL: condition.append(SQL_COMPARE_NOT_EQUAL); break; } condition.append(WHITESPACE); } @Override public void endVisitCompareRightOperand(Compare.Operator operator) { //Not applicable } @Override public void beginVisitIsNull(String streamId) { condition.append(SQL_IS_NULL).append(WHITESPACE); } @Override public void endVisitIsNull(String streamId) { //Not applicable } @Override public void beginVisitIn(String storeId) { condition.append(SQL_IN).append(WHITESPACE); } @Override public void endVisitIn(String storeId) { //Not applicable } @Override public void beginVisitConstant(Object value, Attribute.Type type) { String name = this.generateConstantName(); this.placeholders.put(name, new Constant(value, type)); condition.append("[").append(name).append("]").append(WHITESPACE); } @Override public void endVisitConstant(Object value, Attribute.Type type) { //Not applicable } @Override public void beginVisitMath(MathOperator mathOperator) { condition.append(RDBMSTableConstants.OPEN_PARENTHESIS); } @Override public void endVisitMath(MathOperator mathOperator) { condition.append(RDBMSTableConstants.CLOSE_PARENTHESIS); } @Override public void beginVisitMathLeftOperand(MathOperator mathOperator) { //Not applicable } @Override public void endVisitMathLeftOperand(MathOperator mathOperator) { //Not applicable } @Override public void beginVisitMathRightOperand(MathOperator mathOperator) { switch (mathOperator) { case ADD: condition.append(SQL_MATH_ADD); break; case DIVIDE: condition.append(SQL_MATH_DIVIDE); break; case MOD: condition.append(SQL_MATH_MOD); break; case MULTIPLY: condition.append(SQL_MATH_MULTIPLY); break; case SUBTRACT: condition.append(SQL_MATH_SUBTRACT); break; } condition.append(WHITESPACE); } @Override public void endVisitMathRightOperand(MathOperator mathOperator) { //Not applicable } @Override public void beginVisitAttributeFunction(String namespace, String functionName) { if (RDBMSTableUtils.isEmpty(namespace)) { condition.append(functionName).append(RDBMSTableConstants.OPEN_PARENTHESIS); } else { throw new RDBMSTableException("The RDBMS Event table does not support function namespaces, but namespace '" + namespace + "' was specified. Please use functions supported by the defined RDBMS data store."); } } @Override public void endVisitAttributeFunction(String namespace, String functionName) { if (RDBMSTableUtils.isEmpty(namespace)) { condition.append(RDBMSTableConstants.OPEN_PARENTHESIS).append(WHITESPACE); } else { throw new RDBMSTableException("The RDBMS Event table does not support function namespaces, but namespace '" + namespace + "' was specified. Please use functions supported by the defined RDBMS data store."); } } @Override public void beginVisitParameterAttributeFunction(int index) { //Not applicable } @Override public void endVisitParameterAttributeFunction(int index) { //Not applicable } @Override public void beginVisitStreamVariable(String id, String streamId, String attributeName, Attribute.Type type) { String name = this.generateStreamVarName(); this.placeholders.put(name, new Attribute(id, type)); condition.append("[").append(name).append("]").append(WHITESPACE); } @Override public void endVisitStreamVariable(String id, String streamId, String attributeName, Attribute.Type type) { //Not applicable } @Override public void beginVisitStoreVariable(String storeId, String attributeName, Attribute.Type type) { condition.append(this.tableName).append(".").append(attributeName).append(WHITESPACE); } @Override public void endVisitStoreVariable(String storeId, String attributeName, Attribute.Type type) { //Not applicable } /** * Util method for walking through the generated condition string and isolating the parameters which will be filled * in later as part of building the SQL statement. This method will: * (a) eliminate all temporary placeholders and put "?" in their places. * (b) build and maintain a sorted map of ordinals and the coresponding parameters which will fit into the above * places in the PreparedStatement. */ private void parametrizeCondition() { String query = this.condition.toString(); String[] tokens = query.split("\\["); int ordinal = 1; for (String token : tokens) { if (token.contains("]")) { String candidate = token.substring(0, token.indexOf("]")); if (this.placeholders.containsKey(candidate)) { this.parameters.put(ordinal, this.placeholders.get(candidate)); ordinal++; } } } for (String placeholder : this.placeholders.keySet()) { query = query.replace("[" + placeholder + "]", "?"); } this.finalCompiledCondition = query; } /** * Method for generating a temporary placeholder for stream variables. * * @return a placeholder string of known format. */ private String generateStreamVarName() { String name = "strVar" + this.streamVarCount; this.streamVarCount++; return name; } /** * Method for generating a temporary placeholder for constants. * * @return a placeholder string of known format. */ private String generateConstantName() { String name = "const" + this.constantCount; this.constantCount++; return name; } }