/* This file is part of VoltDB.
* Copyright (C) 2008-2014 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;
import java.nio.ByteBuffer;
import org.apache.hadoop_voltpatches.util.PureJavaCrc32C;
import org.voltdb.common.Constants;
import org.voltdb.planner.ActivePlanRepository;
/**
* <p>A simple wrapper of a parameterized SQL statement. VoltDB uses this instead of
* a Java String type for performance reasons and to cache statement meta-data like
* result schema, compiled plan, etc..</p>
*
* <p>SQLStmts are used exclusively in subclasses of {@link VoltProcedure}</p>
*
* @see VoltProcedure
*/
public class SQLStmt {
/**
* Per-fragment info
*/
static class Frag {
final long id;
final byte[] planHash;
final boolean transactional;
Frag(long id, byte[] planHash, boolean transactional) {
this.id = id;
this.planHash = planHash;
this.transactional = transactional;
}
}
// Used for uncompiled SQL.
byte[] sqlText;
String sqlTextStr;
String joinOrder;
// hash of the sql string for determinism checks
byte[] sqlCRC;
byte statementParamJavaTypes[];
Frag aggregator;
Frag collector;
boolean isReplicatedTableDML;
boolean isReadOnly;
boolean inCatalog;
// used to clean up plans
SiteProcedureConnection site;
/**
* Construct a SQLStmt instance from a SQL statement.
*
* @param sqlText Valid VoltDB compliant SQL with question marks as parameter
* place holders.
*/
public SQLStmt(String sqlText) {
this(sqlText, null);
}
/**
* Construct a SQLStmt instance from a SQL statement.
*
* @param sqlText Valid VoltDB compliant SQL with question marks as parameter
* place holders.
* @param joinOrder separated list of tables used by the query specifying the order they should be joined in
*/
public SQLStmt(String sqlText, String joinOrder) {
this(sqlText.getBytes(Constants.UTF8ENCODING), joinOrder);
}
/**
* Construct a SQLStmt instance from a byte array for internal use.
*/
private SQLStmt(byte[] sqlText, String joinOrder) {
this.sqlText = sqlText;
this.joinOrder = joinOrder;
// create a hash for determinism purposes
PureJavaCrc32C crc = new PureJavaCrc32C();
crc.update(sqlText);
// ugly hack to get bytes from an int
this.sqlCRC = ByteBuffer.allocate(4).putInt((int) crc.getValue()).array();
inCatalog = true;
}
/* (non-Javadoc)
* @see java.lang.Object#finalize()
*/
@Override
protected void finalize() throws Throwable {
// I have some doubts as to whether site is ever null, whether it ever should be,
// and whether it should be the determining factor in this cleanup code.
// "site" is only used here and arguably abused as a null/non-null boolean.
// The interesting cases to consider are:
// - the standard SQLStmts that are defined in user VoltProcedure classes.
// Oddly, these can be static members or instance members based on the whim of the user
// -- are SQLStmts in either of these cases "finalized" before site shutdown,
// at a time when refreshing them in the LRU isn't pointless?
// - the dynamic SQLStmts built for ad hoc queries.
// These include client-issued ad hocs and "experimental" stored-proc-issued ad hocs.
// I'm totally guessing that site==null was supposed to distinguish somehow among
// some of these cases but I don't really know, or know which or know why or know how. --paul
if (site != null) {
ActivePlanRepository.decrefPlanFragmentById(aggregator.id);
if (collector != null) {
ActivePlanRepository.decrefPlanFragmentById(collector.id);
}
}
super.finalize();
}
/**
* Factory method to construct a SQLStmt instance from a plan outside the catalog.
*
* @param sqlText Valid VoltDB compliant SQL
* @param aggFragId Site-local id of the aggregator fragment
* @param aggPlanHash 20 byte sha1 hash of the aggregator fragment plan
* @param isAggTransactional Does the aggregator fragment read/write tables?
* @param collectorFragId Site-local id of the collector fragment
* @param collectorPlanHash 20 byte sha1 hash of the collector fragment plan
* @param isCollectorTransactional Does the collector fragment read/write tables?
* @param isReplicatedTableDML Flag set to true if replicated DML
* @param isReadOnly Is SQL read only?
* @param params Description of parameters expected by the statement
* @param site SPC used for cleanup of plans
* @return SQLStmt object with plan added
*/
static SQLStmt createWithPlan(byte[] sqlText,
long aggFragId,
byte[] aggPlanHash,
boolean isAggTransactional,
long collectorFragId,
byte[] collectorPlanHash,
boolean isCollectorTransactional,
boolean isReplicatedTableDML,
boolean isReadOnly,
VoltType[] params,
SiteProcedureConnection site) {
SQLStmt stmt = new SQLStmt(sqlText, null);
stmt.aggregator = new SQLStmt.Frag(aggFragId, aggPlanHash, isAggTransactional);
if (collectorFragId > 0) {
stmt.collector = new SQLStmt.Frag(collectorFragId, collectorPlanHash, isCollectorTransactional);
}
/*
* Fill out the parameter types
*/
if (params != null) {
stmt.statementParamJavaTypes = new byte[params.length];
for (int i = 0; i < params.length; i++) {
stmt.statementParamJavaTypes[i] = params[i].getValue();
}
}
stmt.isReadOnly = isReadOnly;
stmt.isReplicatedTableDML = isReplicatedTableDML;
stmt.inCatalog = false;
stmt.site = site;
return stmt;
}
/**
* Get the text of the SQL statement represented.
*
* @return String containing the text of the SQL statement represented.
*/
public String getText() {
if (sqlTextStr == null) {
sqlTextStr = new String(sqlText, Constants.UTF8ENCODING);
}
return sqlTextStr;
}
/**
* Get the join order hint supplied in the constructor.
*
* @return String containing the join order hint.
*/
public String getJoinOrder() {
return joinOrder;
}
}