2 Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 package com.mysql.clusterj.core.metadata;
20 import com.mysql.clusterj.core.query.CandidateIndexImpl;
21 import com.mysql.clusterj.core.spi.DomainTypeHandler;
22 import com.mysql.clusterj.core.store.Dictionary;
23 import com.mysql.clusterj.core.store.Index;
24 import com.mysql.clusterj.core.util.I18NHelper;
25 import com.mysql.clusterj.core.util.Logger;
26 import com.mysql.clusterj.core.util.LoggerFactoryService;
28 import java.util.Arrays;
30 /** IndexHandlerImpl represents indices defined in the cluster.
31 * One instance of this class represents either an ordered index or a unique
32 * hash index. So a unique index that results in creating two indices will
33 * be represented by two instances of IndexHandlerImpl.
34 * The Dictionary access to unique indexes requires the suffix "$unique"
35 * to be appended to the name, but the NdbTransaction access requires that
36 * the suffix not be used. This class is responsible for mediating the
39 * The simple case is one index => one field => one column.
41 * For ClusterJ and JPA, indexes can also support multiple columns, with each column
42 * mapped to one field. This pattern is used for compound primary keys. In this case,
43 * there is one instance of IndexHandlerImpl for each index, and the columnNames
44 * and fields have the same cardinality.
45 * This is one index => multiple (one field => one column)
47 * For JPA, indexes can also support one field mapped to multiple columns, which is
48 * the pattern used for compound foreign keys to represent relationships.
49 * In this case, there is a single instance of IndexHandlerImpl for each index. The
50 * columnNames lists the columns covered by the index, and there is one field. The
51 * field manages an instance of the object id class for the relationship.
52 * This is one index => one field => multiple columns.
56 public class IndexHandlerImpl {
58 /** My message translator */
59 static final I18NHelper local = I18NHelper.getInstance(IndexHandlerImpl.class);
62 static final Logger logger = LoggerFactoryService.getFactory().getInstance(IndexHandlerImpl.class);
64 /** The unique suffix. */
65 static final String UNIQUE_SUFFIX = "$unique";
68 protected String className;
71 protected String tableName;
73 /** The indexName of the index. */
74 private String indexName;
76 /** The store Index of this index. */
77 protected Index storeIndex;
79 /** This is a unique index? */
80 protected boolean unique = true;
82 /** The fields (corresponding to the columnNames) in the index. */
83 protected AbstractDomainFieldHandlerImpl[] fields;
85 /** The columnNames in the index. */
86 protected final String[] columnNames;
88 /** This index is usable (all fields are mapped) */
89 private boolean usable = true;
91 /** The reason the index is not usable */
92 private String reason = null;
94 /** Construct an IndexHandlerImpl from an index name and column names.
95 * This constructor is used when the column handlers are not yet known.
96 * @param domainTypeHandler the domain type handler
97 * @param dictionary the dictionary for validation
98 * @param storeIndex the store index
99 * @param columnNames the column names for the index
101 public IndexHandlerImpl(DomainTypeHandler<?> domainTypeHandler,
102 Dictionary dictionary, Index storeIndex, String[] columnNames) {
103 this.className = domainTypeHandler.getName();
104 this.storeIndex = storeIndex;
105 this.indexName = storeIndex.getName();
106 this.tableName = domainTypeHandler.getTableName();
107 this.columnNames = columnNames;
108 int numberOfColumns = columnNames.length;
109 // the fields are not yet known; will be filled later
110 this.fields = new AbstractDomainFieldHandlerImpl[numberOfColumns];
111 this.unique = storeIndex.isUnique();
112 if (logger.isDebugEnabled()) logger.debug(toString());
115 /** Construct an IndexHandlerImpl from an index declared on a field.
116 * There may be more than one column in the index; the columns are taken from the
117 * columns mapped by the field.
118 * @param domainTypeHandler the domain type handler
119 * @param dictionary the Dictionary
120 * @param indexName the index name
121 * @param fmd the DomainFieldHandlerImpl that corresponds to the column
123 public IndexHandlerImpl(DomainTypeHandler<?> domainTypeHandler,
124 Dictionary dictionary, String indexName, AbstractDomainFieldHandlerImpl fmd) {
125 this.className = domainTypeHandler.getName();
126 this.indexName = indexName;
127 this.tableName = domainTypeHandler.getTableName();
128 this.storeIndex = getIndex(dictionary, tableName, indexName);
129 this.unique = isUnique(storeIndex);
130 this.columnNames = fmd.getColumnNames();
131 this.fields = new AbstractDomainFieldHandlerImpl[]{fmd};
132 if (logger.isDebugEnabled()) logger.debug(toString());
135 /** Create a CandidateIndexImpl from this IndexHandlerImpl.
136 * The CandidateIndexImpl is used to decide on the strategy for a query.
139 public CandidateIndexImpl toCandidateIndexImpl() {
141 return CandidateIndexImpl.getIndexForNullWhereClause();
143 return new CandidateIndexImpl(
144 className, storeIndex, unique, fields);
149 public String toString() {
150 StringBuffer buffer = new StringBuffer();
151 buffer.append("IndexHandler for class ");
152 buffer.append(className);
153 buffer.append(" index: ");
154 buffer.append(indexName);
155 buffer.append(" unique: ");
156 buffer.append(unique);
157 buffer.append(" columns: ");
158 buffer.append(Arrays.toString(columnNames));
159 return buffer.toString();
162 /** Set the DomainFieldHandlerImpl once it's known.
164 * @param i the index into the fields array
165 * @param fmd the DomainFieldHandlerImpl for the corresponding column
167 public void setDomainFieldHandlerFor(int i, AbstractDomainFieldHandlerImpl fmd) {
169 fmd.validateIndexType(indexName, unique);
172 /** Accessor for columnNames. */
173 public String[] getColumnNames() {
177 /** Validate that all columnNames have fields.
180 public void assertAllColumnsHaveFields() {
181 for (int i = 0; i < columnNames.length; ++i) {
182 AbstractDomainFieldHandlerImpl fmd = fields[i];
183 if (fmd == null || !(columnNames[i].equals(fmd.getColumnName()))) {
185 reason = local.message(
186 "ERR_Index_Mismatch", className, indexName, columnNames[i]);
191 protected boolean isUnique(Index storeIndex) {
192 return storeIndex.isUnique();
195 public boolean isUsable() {
199 public String getReason() {
203 protected Index getIndex(Dictionary dictionary,
204 String tableName, String indexName) {
205 return dictionary.getIndex(indexName, tableName, indexName);
208 public String getIndexName() {