/* *************************************************************************************** * Copyright (C) 2006 EsperTech, Inc. All rights reserved. * * http://www.espertech.com/esper * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * *************************************************************************************** */ package com.espertech.esper.epl.lookup; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.collection.Pair; import com.espertech.esper.core.context.util.AgentInstanceContext; import com.espertech.esper.epl.expression.core.ExprValidationException; import com.espertech.esper.epl.index.service.EventAdvancedIndexProvisionDesc; import com.espertech.esper.epl.join.hint.IndexHintInstruction; import com.espertech.esper.epl.join.plan.QueryPlanIndexItem; import com.espertech.esper.epl.join.table.EventTable; import com.espertech.esper.epl.join.table.EventTableAndNamePair; import com.espertech.esper.epl.join.table.EventTableUtil; import java.util.*; /** * A repository of index tables for use with anything that * may use the indexes to correlate triggering events with indexed events. * <p> * Maintains index tables and keeps a reference count for user. Allows reuse of indexes for multiple * deleting statements. */ public class EventTableIndexRepository { private final List<EventTable> tables; private final Map<IndexMultiKey, EventTableIndexRepositoryEntry> tableIndexesRefCount; private final HashMap<String, EventTable> explicitIndexes; private final EventTableIndexMetadata eventTableIndexMetadata; /** * Ctor. * @param eventTableIndexMetadata metadata for index */ public EventTableIndexRepository(EventTableIndexMetadata eventTableIndexMetadata) { tables = new ArrayList<EventTable>(); tableIndexesRefCount = new HashMap<IndexMultiKey, EventTableIndexRepositoryEntry>(); explicitIndexes = new HashMap<String, EventTable>(); this.eventTableIndexMetadata = eventTableIndexMetadata; } public EventTableIndexMetadata getEventTableIndexMetadata() { return eventTableIndexMetadata; } public Pair<IndexMultiKey, EventTableAndNamePair> addExplicitIndexOrReuse( boolean unique, List<IndexedPropDesc> hashProps, List<IndexedPropDesc> btreeProps, EventAdvancedIndexProvisionDesc advancedIndexProvisionDesc, Iterable<EventBean> prefilledEvents, EventType indexedType, String indexName, AgentInstanceContext agentInstanceContext, Object optionalSerde) { if (hashProps.isEmpty() && btreeProps.isEmpty() && advancedIndexProvisionDesc == null) { throw new IllegalArgumentException("Invalid zero element list for hash and btree columns"); } // Get an existing table, if any, matching the exact requirement IndexMultiKey indexPropKeyMatch = EventTableIndexUtil.findExactMatchNameAndType(tableIndexesRefCount.keySet(), unique, hashProps, btreeProps, advancedIndexProvisionDesc == null ? null : advancedIndexProvisionDesc.getIndexDesc()); if (indexPropKeyMatch != null) { EventTableIndexRepositoryEntry refTablePair = tableIndexesRefCount.get(indexPropKeyMatch); return new Pair<IndexMultiKey, EventTableAndNamePair>(indexPropKeyMatch, new EventTableAndNamePair(refTablePair.getTable(), refTablePair.getOptionalIndexName())); } return addIndex(unique, hashProps, btreeProps, advancedIndexProvisionDesc, prefilledEvents, indexedType, indexName, false, agentInstanceContext, optionalSerde); } public void addIndex(IndexMultiKey indexMultiKey, EventTableIndexRepositoryEntry entry) { tableIndexesRefCount.put(indexMultiKey, entry); tables.add(entry.getTable()); } /** * Returns a list of current index tables in the repository. * * @return index tables */ public List<EventTable> getTables() { return tables; } /** * Destroy indexes. */ public void destroy() { for (EventTable table : tables) { table.destroy(); } tables.clear(); tableIndexesRefCount.clear(); } public Pair<IndexMultiKey, EventTableAndNamePair> findTable(Set<String> keyPropertyNames, Set<String> rangePropertyNames, List<IndexHintInstruction> optionalIndexHintInstructions) { Pair<IndexMultiKey, EventTableIndexEntryBase> pair = EventTableIndexUtil.findIndexBestAvailable(tableIndexesRefCount, keyPropertyNames, rangePropertyNames, optionalIndexHintInstructions); if (pair == null) { return null; } EventTable tableFound = ((EventTableIndexRepositoryEntry) pair.getSecond()).getTable(); return new Pair<IndexMultiKey, EventTableAndNamePair>(pair.getFirst(), new EventTableAndNamePair(tableFound, pair.getSecond().getOptionalIndexName())); } public IndexMultiKey[] getIndexDescriptors() { Set<IndexMultiKey> keySet = tableIndexesRefCount.keySet(); return keySet.toArray(new IndexMultiKey[keySet.size()]); } public Map<IndexMultiKey, EventTableIndexRepositoryEntry> getTableIndexesRefCount() { return tableIndexesRefCount; } public void validateAddExplicitIndex(String explicitIndexName, QueryPlanIndexItem explicitIndexDesc, EventType eventType, Iterable<EventBean> dataWindowContents, AgentInstanceContext agentInstanceContext, boolean allowIndexExists, Object optionalSerde) throws ExprValidationException { if (explicitIndexes.containsKey(explicitIndexName)) { if (allowIndexExists) { return; } throw new ExprValidationException("Index by name '" + explicitIndexName + "' already exists"); } addExplicitIndex(explicitIndexName, explicitIndexDesc, eventType, dataWindowContents, agentInstanceContext, optionalSerde); } public void addExplicitIndex(String explicitIndexName, QueryPlanIndexItem desc, EventType eventType, Iterable<EventBean> dataWindowContents, AgentInstanceContext agentInstanceContext, Object optionalSerde) { Pair<IndexMultiKey, EventTableAndNamePair> pair = addExplicitIndexOrReuse(desc.isUnique(), desc.getHashPropsAsList(), desc.getBtreePropsAsList(), desc.getAdvancedIndexProvisionDesc(), dataWindowContents, eventType, explicitIndexName, agentInstanceContext, optionalSerde); explicitIndexes.put(explicitIndexName, pair.getSecond().getEventTable()); } public EventTable getExplicitIndexByName(String indexName) { return explicitIndexes.get(indexName); } public EventTable getIndexByDesc(IndexMultiKey indexKey) { EventTableIndexRepositoryEntry entry = tableIndexesRefCount.get(indexKey); if (entry == null) { return null; } return entry.getTable(); } private Pair<IndexMultiKey, EventTableAndNamePair> addIndex(boolean unique, List<IndexedPropDesc> hashProps, List<IndexedPropDesc> btreeProps, EventAdvancedIndexProvisionDesc advancedIndexProvisionDesc, Iterable<EventBean> prefilledEvents, EventType indexedType, String indexName, boolean mustCoerce, AgentInstanceContext agentInstanceContext, Object optionalSerde) { // not resolved as full match and not resolved as unique index match, allocate IndexMultiKey indexPropKey = new IndexMultiKey(unique, hashProps, btreeProps, advancedIndexProvisionDesc == null ? null : advancedIndexProvisionDesc.getIndexDesc()); IndexedPropDesc[] indexedPropDescs = hashProps.toArray(new IndexedPropDesc[hashProps.size()]); String[] indexProps = IndexedPropDesc.getIndexProperties(indexedPropDescs); Class[] indexCoercionTypes = IndexedPropDesc.getCoercionTypes(indexedPropDescs); if (!mustCoerce) { indexCoercionTypes = null; } IndexedPropDesc[] rangePropDescs = btreeProps.toArray(new IndexedPropDesc[btreeProps.size()]); String[] rangeProps = IndexedPropDesc.getIndexProperties(rangePropDescs); Class[] rangeCoercionTypes = IndexedPropDesc.getCoercionTypes(rangePropDescs); QueryPlanIndexItem indexItem = new QueryPlanIndexItem(indexProps, indexCoercionTypes, rangeProps, rangeCoercionTypes, unique, advancedIndexProvisionDesc); EventTable table = EventTableUtil.buildIndex(agentInstanceContext, 0, indexItem, indexedType, true, unique, indexName, optionalSerde, false); // fill table since its new EventBean[] events = new EventBean[1]; for (EventBean prefilledEvent : prefilledEvents) { events[0] = prefilledEvent; table.add(events, agentInstanceContext); } // add table tables.add(table); // add index, reference counted tableIndexesRefCount.put(indexPropKey, new EventTableIndexRepositoryEntry(indexName, table)); return new Pair<IndexMultiKey, EventTableAndNamePair>(indexPropKey, new EventTableAndNamePair(table, indexName)); } public String[] getExplicitIndexNames() { Set<String> names = explicitIndexes.keySet(); return names.toArray(new String[names.size()]); } public void removeIndex(IndexMultiKey index) { EventTableIndexRepositoryEntry entry = tableIndexesRefCount.remove(index); if (entry != null) { tables.remove(entry.getTable()); if (entry.getOptionalIndexName() != null) { explicitIndexes.remove(entry.getOptionalIndexName()); } entry.getTable().destroy(); } } public IndexMultiKey getIndexByName(String indexName) { for (Map.Entry<IndexMultiKey, EventTableIndexRepositoryEntry> entry : tableIndexesRefCount.entrySet()) { if (entry.getValue().getOptionalIndexName().equals(indexName)) { return entry.getKey(); } } return null; } public void removeExplicitIndex(String indexName) { EventTable eventTable = explicitIndexes.remove(indexName); if (eventTable != null) { eventTable.destroy(); } } }