blob: 9754313efef476a8e8c126026701ca0c7f927f26 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.apache.phoenix.compile;
import java.sql.SQLException;
import java.text.Format;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.util.Pair;
import org.apache.phoenix.jdbc.PhoenixConnection;
import org.apache.phoenix.jdbc.PhoenixStatement;
import org.apache.phoenix.monitoring.OverAllQueryMetrics;
import org.apache.phoenix.monitoring.ReadMetricQueue;
import org.apache.phoenix.parse.SelectStatement;
import org.apache.phoenix.query.QueryConstants;
import org.apache.phoenix.query.QueryServices;
import org.apache.phoenix.schema.MetaDataClient;
import org.apache.phoenix.schema.PColumn;
import org.apache.phoenix.schema.PTable;
import org.apache.phoenix.schema.PTableType;
import org.apache.phoenix.schema.TableRef;
import org.apache.phoenix.util.DateUtil;
import org.apache.phoenix.util.NumberUtil;
import org.apache.phoenix.util.ReadOnlyProps;
import com.google.common.collect.Maps;
/**
*
* Class that keeps common state used across processing the various clauses in a
* top level JDBC statement such as SELECT, UPSERT, DELETE, etc.
*
*
* @since 0.1
*/
public class StatementContext {
private ColumnResolver resolver;
private final BindManager binds;
private final Scan scan;
private final ExpressionManager expressions;
private final AggregationManager aggregates;
private final String dateFormat;
private final Format dateFormatter;
private final String timeFormat;
private final Format timeFormatter;
private final String timestampFormat;
private final Format timestampFormatter;
private final TimeZone dateFormatTimeZone;
private final String numberFormat;
private final ImmutableBytesWritable tempPtr;
private final PhoenixStatement statement;
private final Map<PColumn, Integer> dataColumns;
private long currentTime = QueryConstants.UNSET_TIMESTAMP;
private ScanRanges scanRanges = ScanRanges.EVERYTHING;
private final SequenceManager sequences;
private TableRef currentTable;
private List<Pair<byte[], byte[]>> whereConditionColumns;
private Map<SelectStatement, Object> subqueryResults;
private final ReadMetricQueue readMetricsQueue;
private final OverAllQueryMetrics overAllQueryMetrics;
public StatementContext(PhoenixStatement statement) {
this(statement, new Scan());
}
/**
* Constructor that lets you override whether or not to collect request level metrics.
*/
public StatementContext(PhoenixStatement statement, boolean collectRequestLevelMetrics) {
this(statement, FromCompiler.EMPTY_TABLE_RESOLVER, new Scan(), new SequenceManager(statement), collectRequestLevelMetrics);
}
public StatementContext(PhoenixStatement statement, Scan scan) {
this(statement, FromCompiler.EMPTY_TABLE_RESOLVER, new Scan(), new SequenceManager(statement));
}
public StatementContext(PhoenixStatement statement, ColumnResolver resolver) {
this (statement, resolver, new Scan(), new SequenceManager(statement));
}
public StatementContext(PhoenixStatement statement, ColumnResolver resolver, Scan scan, SequenceManager seqManager) {
this(statement, resolver, scan, seqManager, statement.getConnection().isRequestLevelMetricsEnabled());
}
public StatementContext(PhoenixStatement statement, ColumnResolver resolver, Scan scan, SequenceManager seqManager, boolean isRequestMetricsEnabled) {
this.statement = statement;
this.resolver = resolver;
this.scan = scan;
this.sequences = seqManager;
this.binds = new BindManager(statement.getParameters());
this.aggregates = new AggregationManager();
this.expressions = new ExpressionManager();
PhoenixConnection connection = statement.getConnection();
ReadOnlyProps props = connection.getQueryServices().getProps();
this.dateFormat = props.get(QueryServices.DATE_FORMAT_ATTRIB, DateUtil.DEFAULT_DATE_FORMAT);
this.dateFormatter = DateUtil.getDateFormatter(dateFormat);
this.timeFormat = props.get(QueryServices.TIME_FORMAT_ATTRIB, DateUtil.DEFAULT_TIME_FORMAT);
this.timeFormatter = DateUtil.getTimeFormatter(timeFormat);
this.timestampFormat = props.get(QueryServices.TIMESTAMP_FORMAT_ATTRIB, DateUtil.DEFAULT_TIMESTAMP_FORMAT);
this.timestampFormatter = DateUtil.getTimestampFormatter(timestampFormat);
this.dateFormatTimeZone = DateUtil.getTimeZone(props.get(QueryServices.DATE_FORMAT_TIMEZONE_ATTRIB,
DateUtil.DEFAULT_TIME_ZONE_ID));
this.numberFormat = props.get(QueryServices.NUMBER_FORMAT_ATTRIB, NumberUtil.DEFAULT_NUMBER_FORMAT);
this.tempPtr = new ImmutableBytesWritable();
this.currentTable = resolver != null && !resolver.getTables().isEmpty() ? resolver.getTables().get(0) : null;
this.whereConditionColumns = new ArrayList<Pair<byte[], byte[]>>();
this.dataColumns = this.currentTable == null ? Collections.<PColumn, Integer> emptyMap() : Maps
.<PColumn, Integer> newLinkedHashMap();
this.subqueryResults = Maps.<SelectStatement, Object> newHashMap();
this.readMetricsQueue = new ReadMetricQueue(isRequestMetricsEnabled);
this.overAllQueryMetrics = new OverAllQueryMetrics(isRequestMetricsEnabled);
}
/**
* build map from dataColumn to what will be its position in single KeyValue value bytes
* returned from the coprocessor that joins from the index row back to the data row.
* @param column
* @return
*/
public int getDataColumnPosition(PColumn column) {
Integer pos = dataColumns.get(column);
if (pos == null) {
pos = dataColumns.size();
dataColumns.put(column, pos);
}
return pos;
}
/**
* @return return set of data columns.
*/
public Set<PColumn> getDataColumns() {
return dataColumns.keySet();
}
/**
* @return map of data columns and their positions.
*/
public Map<PColumn, Integer> getDataColumnsMap() {
return dataColumns;
}
public String getDateFormat() {
return dateFormat;
}
public TimeZone getDateFormatTimeZone() {
return dateFormatTimeZone;
}
public Format getDateFormatter() {
return dateFormatter;
}
public String getTimeFormat() {
return timeFormat;
}
public Format getTimeFormatter() {
return timeFormatter;
}
public String getTimestampFormat() {
return timestampFormat;
}
public Format getTimestampFormatter() {
return timestampFormatter;
}
public String getNumberFormat() {
return numberFormat;
}
public Scan getScan() {
return scan;
}
public BindManager getBindManager() {
return binds;
}
public TableRef getCurrentTable() {
return currentTable;
}
public void setCurrentTable(TableRef table) {
this.currentTable = table;
}
public AggregationManager getAggregationManager() {
return aggregates;
}
public ColumnResolver getResolver() {
return resolver;
}
public void setResolver(ColumnResolver resolver) {
this.resolver = resolver;
}
public ExpressionManager getExpressionManager() {
return expressions;
}
public ImmutableBytesWritable getTempPtr() {
return tempPtr;
}
public ScanRanges getScanRanges() {
return this.scanRanges;
}
public void setScanRanges(ScanRanges scanRanges) {
this.scanRanges = scanRanges;
scanRanges.initializeScan(scan);
}
public PhoenixConnection getConnection() {
return statement.getConnection();
}
public PhoenixStatement getStatement() {
return statement;
}
public long getCurrentTime() throws SQLException {
long ts = this.getCurrentTable().getTimeStamp();
// if the table is transactional then it is only resolved once per query, so we can't use the table timestamp
if (this.getCurrentTable().getTable().getType() != PTableType.PROJECTED && !this
.getCurrentTable().getTable().isTransactional() && ts != QueryConstants
.UNSET_TIMESTAMP) {
return ts;
}
if (currentTime != QueryConstants.UNSET_TIMESTAMP) {
return currentTime;
}
/*
* For an UPSERT VALUES where autocommit off, we won't hit the server until the commit.
* However, if the statement has a CURRENT_DATE() call as a value, we need to know the
* current time at execution time. In that case, we'll call MetaDataClient.updateCache
* purely to bind the current time based on the server time.
*/
PTable table = this.getCurrentTable().getTable();
PhoenixConnection connection = getConnection();
MetaDataClient client = new MetaDataClient(connection);
currentTime = client.getCurrentTime(table.getSchemaName().getString(), table.getTableName().getString());
return currentTime;
}
public SequenceManager getSequenceManager(){
return sequences;
}
public void addWhereConditionColumn(byte[] cf, byte[] q) {
whereConditionColumns.add(new Pair<byte[], byte[]>(cf, q));
}
public List<Pair<byte[], byte[]>> getWhereConditionColumns() {
return whereConditionColumns;
}
public boolean isSubqueryResultAvailable(SelectStatement select) {
return subqueryResults.containsKey(select);
}
public Object getSubqueryResult(SelectStatement select) {
return subqueryResults.get(select);
}
public void setSubqueryResult(SelectStatement select, Object result) {
subqueryResults.put(select, result);
}
public ReadMetricQueue getReadMetricsQueue() {
return readMetricsQueue;
}
public OverAllQueryMetrics getOverallQueryMetrics() {
return overAllQueryMetrics;
}
}