blob: 63ac318fd841f388e9ecb85543cae1d40f11e263 [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.ignite.internal.processors.query.h2.database;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManager;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.tree.CacheDataRowStore;
import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
import org.apache.ignite.internal.processors.query.h2.H2Utils;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
import org.apache.ignite.internal.processors.query.h2.opt.QueryContext;
import org.apache.ignite.internal.util.lang.GridCursor;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.spi.indexing.IndexingQueryCacheFilter;
import org.apache.ignite.spi.indexing.IndexingQueryFilter;
import org.h2.engine.Session;
import org.h2.index.Cursor;
import org.h2.index.IndexType;
import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.TableFilter;
/**
*
*/
public class H2PkHashIndex extends GridH2IndexBase {
/** */
private final GridCacheContext cctx;
/** */
private final int segments;
/**
* @param cctx Cache context.
* @param tbl Table.
* @param name Index name.
* @param colsList Index columns.
* @param segments Segments.
*/
public H2PkHashIndex(
GridCacheContext<?, ?> cctx,
GridH2Table tbl,
String name,
List<IndexColumn> colsList,
int segments
) {
super(
tbl,
name,
GridH2IndexBase.columnsArray(tbl, colsList),
IndexType.createPrimaryKey(false, true));
assert segments > 0 : segments;
this.cctx = cctx;
this.segments = segments;
}
/** {@inheritDoc} */
@Override public int segmentsCount() {
return segments;
}
/** {@inheritDoc} */
@Override public Cursor find(Session ses, final SearchRow lower, final SearchRow upper) {
IndexingQueryCacheFilter filter = null;
MvccSnapshot mvccSnapshot = null;
QueryContext qctx = H2Utils.context(ses);
int seg = 0;
if (qctx != null) {
IndexingQueryFilter f = qctx.filter();
filter = f != null ? f.forCache(getTable().cacheName()) : null;
mvccSnapshot = qctx.mvccSnapshot();
seg = qctx.segment();
}
KeyCacheObject lowerObj = lower != null ? cctx.toCacheKeyObject(lower.getValue(0).getObject()) : null;
KeyCacheObject upperObj = upper != null ? cctx.toCacheKeyObject(upper.getValue(0).getObject()) : null;
try {
CacheDataRowStore.setSkipVersion(true);
Collection<GridCursor<? extends CacheDataRow>> cursors = new ArrayList<>();
for (IgniteCacheOffheapManager.CacheDataStore store : cctx.offheap().cacheDataStores()) {
int part = store.partId();
if (segmentForPartition(part) != seg)
continue;
if (filter == null || filter.applyPartition(part))
cursors.add(store.cursor(cctx.cacheId(), lowerObj, upperObj, null, mvccSnapshot));
}
return new H2PkHashIndexCursor(cursors.iterator());
}
catch (IgniteCheckedException e) {
throw DbException.convert(e);
}
finally {
CacheDataRowStore.setSkipVersion(false);
}
}
/** {@inheritDoc} */
@Override public boolean canScan() {
return false;
}
/** {@inheritDoc} */
@Override public H2CacheRow put(H2CacheRow row) {
// Should not be called directly. Rows are inserted into underlying cache data stores.
assert false;
throw DbException.getUnsupportedException("put");
}
/** {@inheritDoc} */
@Override public boolean putx(H2CacheRow row) {
// Should not be called directly. Rows are inserted into underlying cache data stores.
assert false;
throw DbException.getUnsupportedException("putx");
}
/** {@inheritDoc} */
@Override public boolean removex(SearchRow row) {
// Should not be called directly. Rows are removed from underlying cache data stores.
assert false;
throw DbException.getUnsupportedException("removex");
}
/** {@inheritDoc} */
@Override public double getCost(
Session ses,
int[] masks,
TableFilter[] filters,
int filter,
SortOrder sortOrder,
HashSet<Column> allColumnsSet
) {
return Double.MAX_VALUE;
}
/** {@inheritDoc} */
@Override public long totalRowCount(IndexingQueryCacheFilter partsFilter) {
CacheDataRowStore.setSkipVersion(true);
try {
Collection<GridCursor<? extends CacheDataRow>> cursors = new ArrayList<>();
for (IgniteCacheOffheapManager.CacheDataStore store : cctx.offheap().cacheDataStores()) {
int part = store.partId();
if (partsFilter == null || partsFilter.applyPartition(part))
cursors.add(store.cursor(cctx.cacheId()));
}
Cursor pkHashCursor = new H2PkHashIndexCursor(cursors.iterator());
long res = 0;
while (pkHashCursor.next())
res++;
return res;
}
catch (IgniteCheckedException e) {
throw U.convertException(e);
}
finally {
CacheDataRowStore.setSkipVersion(false);
}
}
/** {@inheritDoc} */
@Override public long getRowCount(Session ses) {
Cursor cursor = find(ses, null, null);
long res = 0;
while (cursor.next())
res++;
return res;
}
/** {@inheritDoc} */
@Override public boolean canGetFirstOrLast() {
return false;
}
/** {@inheritDoc} */
@Override public Cursor findFirstOrLast(Session ses, boolean b) {
throw new UnsupportedOperationException();
}
/**
* Cursor.
*/
private class H2PkHashIndexCursor implements Cursor {
/** */
private final GridH2RowDescriptor desc;
/** */
private final Iterator<GridCursor<? extends CacheDataRow>> iter;
/** */
private GridCursor<? extends CacheDataRow> curr;
/** Creation time of this cursor to check if some row is expired. */
private final long time;
/**
* @param iter Cursors iterator.
*/
private H2PkHashIndexCursor(Iterator<GridCursor<? extends CacheDataRow>> iter) {
assert iter != null;
this.iter = iter;
desc = rowDescriptor();
time = U.currentTimeMillis();
}
/** {@inheritDoc} */
@Override public Row get() {
try {
return new H2CacheRow(desc, curr.get());
}
catch (IgniteCheckedException e) {
throw DbException.convert(e);
}
}
/** {@inheritDoc} */
@Override public SearchRow getSearchRow() {
return get();
}
/** {@inheritDoc} */
@Override public boolean next() {
try {
CacheDataRowStore.setSkipVersion(true);
GridQueryTypeDescriptor type = desc.type();
for (;;) {
if (curr != null) {
while (curr.next()) {
CacheDataRow row = curr.get();
// Need to filter rows by value type because in a single cache
// we can have multiple indexed types.
// Also need to skip expired rows.
if (type.matchType(row.value()) && !wasExpired(row))
return true;
}
}
if (!iter.hasNext())
return false;
curr = iter.next();
}
}
catch (IgniteCheckedException e) {
throw DbException.convert(e);
}
finally {
CacheDataRowStore.setSkipVersion(false);
}
}
/**
* @param row to check.
* @return {@code true} if row was expired at the moment this cursor was created; {@code false} if not or if
* expire time is not set for this cursor.
*/
private boolean wasExpired(CacheDataRow row) {
return row.expireTime() > 0 && row.expireTime() <= time;
}
/** {@inheritDoc} */
@Override public boolean previous() {
throw DbException.getUnsupportedException("previous");
}
}
}