/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
*/
package org.voltdb.expressions;
import java.util.ArrayList;
import java.util.List;
import org.json_voltpatches.JSONArray;
import org.json_voltpatches.JSONException;
import org.json_voltpatches.JSONObject;
import org.json_voltpatches.JSONStringer;
import org.voltdb.VoltType;
import org.voltdb.catalog.Database;
import org.voltdb.planner.AbstractParsedStmt;
import org.voltdb.plannodes.AbstractPlanNode;
/**
* Base class to represent sub-select, row, and scalar expressions
* The value type is always a BIGINT because the expression's eval method on the EE side
* returns the subquery id as a result.
* This id can be used by others expressions to access the actual subquery results
*/
public abstract class AbstractSubqueryExpression extends AbstractExpression {
public static final String SUBQUERY_TAG = "Subquery_";
public enum Members {
SUBQUERY_ID,
SUBQUERY_ROOT_NODE_ID,
PARAM_IDX;
}
// subquery unique id
protected int m_subqueryId;
// subquery root plan node id
protected int m_subqueryNodeId = -1;
// subquery root plan node
protected AbstractPlanNode m_subqueryNode = null;
// List of correlated parameter indexes that originate at the immediate parent's level
// and need to be set by this SubqueryExpression on the EE side prior to the evaluation
private List<Integer> m_parameterIdxList = new ArrayList<Integer>();
protected AbstractSubqueryExpression() {
m_valueType = VoltType.BIGINT;
m_valueSize = m_valueType.getLengthInBytesForFixedTypes();
}
public int getSubqueryId() {
return m_subqueryId;
}
public int getSubqueryNodeId() {
return (m_subqueryNode != null) ? m_subqueryNode.getPlanNodeId() : m_subqueryNodeId;
}
public AbstractPlanNode getSubqueryNode() {
return m_subqueryNode;
}
public void setSubqueryNode(AbstractPlanNode subqueryNode) {
assert(subqueryNode != null);
m_subqueryNode = subqueryNode;
resetSubqueryNodeId();
}
public void resetSubqueryNodeId() {
assert(m_subqueryNode != null);
m_subqueryNodeId = m_subqueryNode.getPlanNodeId();
}
public List<Integer> getParameterIdxList() {
return m_parameterIdxList;
}
// Create a matching PVE for this expression to be used on the EE side
// to get the original expression value
protected void addCorrelationParameterValueExpression(AbstractExpression expr, List<AbstractExpression> pves) {
int paramIdx = AbstractParsedStmt.NEXT_PARAMETER_ID++;
m_parameterIdxList.add(paramIdx);
ParameterValueExpression pve = new ParameterValueExpression(paramIdx, expr);
pves.add(pve);
}
protected void addArgumentParameter(Integer paramIdx, AbstractExpression expr) {
m_args.add(expr);
m_parameterIdxList.add(paramIdx);
}
public int overrideSubqueryNodeIds(int newId) {
assert(m_subqueryNode != null);
newId = m_subqueryNode.overrideId(newId);
resetSubqueryNodeId();
return newId;
}
@Override
public AbstractSubqueryExpression clone() {
AbstractSubqueryExpression clone = (AbstractSubqueryExpression) super.clone();
clone.m_parameterIdxList = new ArrayList<>(m_parameterIdxList);
return clone;
}
@Override
public void validate() throws Exception {
super.validate();
if (m_subqueryNode != null && m_subqueryNode.getPlanNodeId() != m_subqueryNodeId)
throw new Exception("ERROR: A subquery plan node id mismatch");
}
@Override
public boolean equals(Object obj) {
if (super.equals(obj) && obj instanceof AbstractSubqueryExpression) {
AbstractSubqueryExpression other = (AbstractSubqueryExpression) obj;
// Expressions are equal if they have the same subquery id (refer to the same subquery)
return m_subqueryId == other.m_subqueryId;
}
return false;
}
@Override
public int hashCode() {
// defer to the superclass, which factors in other attributes
int result = super.hashCode();
result += m_subqueryId;
return result;
}
@Override
public void toJSONString(JSONStringer stringer) throws JSONException {
super.toJSONString(stringer);
stringer.keySymbolValuePair(Members.SUBQUERY_ID.name(), m_subqueryId);
stringer.keySymbolValuePair(Members.SUBQUERY_ROOT_NODE_ID.name(), m_subqueryNodeId);
// Output the correlated parameter ids that originates at this subquery immediate
// parent and need to be set before the evaluation
if (!m_parameterIdxList.isEmpty()) {
stringer.key(Members.PARAM_IDX.name()).array();
for (Integer idx : m_parameterIdxList) {
stringer.value(idx);
}
stringer.endArray();
}
}
@Override
protected void loadFromJSONObject(JSONObject obj) throws JSONException {
m_subqueryId = obj.getInt(Members.SUBQUERY_ID.name());
m_subqueryNodeId = obj.getInt(Members.SUBQUERY_ROOT_NODE_ID.name());
if (obj.has(AbstractExpression.Members.VALUE_TYPE)) {
m_valueType = VoltType.get((byte) obj.getInt(AbstractExpression.Members.VALUE_TYPE));
m_valueSize = m_valueType.getLengthInBytesForFixedTypes();
}
if (obj.has(Members.PARAM_IDX.name())) {
JSONArray paramIdxArray = obj.getJSONArray(Members.PARAM_IDX.name());
int paramSize = paramIdxArray.length();
assert(m_args != null);
for (int i = 0; i < paramSize; ++i) {
m_parameterIdxList.add(paramIdxArray.getInt(i));
}
}
}
@Override
public void finalizeValueTypes() {
// Nothing to do there
}
public void resolveColumnIndexes() {
if (m_subqueryNode != null) {
m_subqueryNode.resolveColumnIndexes();
}
}
public void generateOutputSchema(Database db) {
if (m_subqueryNode != null) {
m_subqueryNode.generateOutputSchema(db);
}
}
@Override
public String getContentDeterminismMessage() {
return null;
}
}