1 /* 2 Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License, version 2.0, 6 as published by the Free Software Foundation. 7 8 This program is also distributed with certain software (including 9 but not limited to OpenSSL) that is licensed under separate terms, 10 as designated in a particular file or component or in included license 11 documentation. The authors of MySQL hereby grant you an additional 12 permission to link the program and your derivative works with the 13 separately licensed software that they have included with MySQL. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License, version 2.0, for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 */ 24 25 package com.mysql.clusterj.core.metadata; 26 27 import com.mysql.clusterj.core.query.CandidateIndexImpl; 28 import com.mysql.clusterj.core.spi.DomainTypeHandler; 29 import com.mysql.clusterj.core.store.Dictionary; 30 import com.mysql.clusterj.core.store.Index; 31 import com.mysql.clusterj.core.util.I18NHelper; 32 import com.mysql.clusterj.core.util.Logger; 33 import com.mysql.clusterj.core.util.LoggerFactoryService; 34 35 import java.util.Arrays; 36 37 /** IndexHandlerImpl represents indices defined in the cluster. 38 * One instance of this class represents either an ordered index or a unique 39 * hash index. So a unique index that results in creating two indices will 40 * be represented by two instances of IndexHandlerImpl. 41 * The Dictionary access to unique indexes requires the suffix "$unique" 42 * to be appended to the name, but the NdbTransaction access requires that 43 * the suffix not be used. This class is responsible for mediating the 44 * difference. 45 * <p> 46 * The simple case is one index => one field => one column. 47 * <p> 48 * For ClusterJ and JPA, indexes can also support multiple columns, with each column 49 * mapped to one field. This pattern is used for compound primary keys. In this case, 50 * there is one instance of IndexHandlerImpl for each index, and the columnNames 51 * and fields have the same cardinality. 52 * This is one index => multiple (one field => one column) 53 * <p> 54 * For JPA, indexes can also support one field mapped to multiple columns, which is 55 * the pattern used for compound foreign keys to represent relationships. 56 * In this case, there is a single instance of IndexHandlerImpl for each index. The 57 * columnNames lists the columns covered by the index, and there is one field. The 58 * field manages an instance of the object id class for the relationship. 59 * This is one index => one field => multiple columns. 60 * <p> 61 * 62 */ 63 public class IndexHandlerImpl { 64 65 /** My message translator */ 66 static final I18NHelper local = I18NHelper.getInstance(IndexHandlerImpl.class); 67 68 /** My logger */ 69 static final Logger logger = LoggerFactoryService.getFactory().getInstance(IndexHandlerImpl.class); 70 71 /** The unique suffix. */ 72 static final String UNIQUE_SUFFIX = "$unique"; 73 74 /** My class */ 75 protected String className; 76 77 /** My table */ 78 protected String tableName; 79 80 /** The indexName of the index. */ 81 private String indexName; 82 83 /** The store Index of this index. */ 84 protected Index storeIndex; 85 86 /** This is a unique index? */ 87 protected boolean unique = true; 88 89 /** The fields (corresponding to the columnNames) in the index. */ 90 protected AbstractDomainFieldHandlerImpl[] fields; 91 92 /** The columnNames in the index. */ 93 protected final String[] columnNames; 94 95 /** This index is usable (all fields are mapped) */ 96 private boolean usable = true; 97 98 /** The reason the index is not usable */ 99 private String reason = null; 100 101 /** Construct an IndexHandlerImpl from an index name and column names. 102 * This constructor is used when the column handlers are not yet known. 103 * @param domainTypeHandler the domain type handler 104 * @param dictionary the dictionary for validation 105 * @param storeIndex the store index 106 * @param columnNames the column names for the index 107 */ IndexHandlerImpl(DomainTypeHandler<?> domainTypeHandler, Dictionary dictionary, Index storeIndex, String[] columnNames)108 public IndexHandlerImpl(DomainTypeHandler<?> domainTypeHandler, 109 Dictionary dictionary, Index storeIndex, String[] columnNames) { 110 this.className = domainTypeHandler.getName(); 111 this.storeIndex = storeIndex; 112 this.indexName = storeIndex.getName(); 113 this.tableName = domainTypeHandler.getTableName(); 114 this.columnNames = columnNames; 115 int numberOfColumns = columnNames.length; 116 // the fields are not yet known; will be filled later 117 this.fields = new AbstractDomainFieldHandlerImpl[numberOfColumns]; 118 this.unique = storeIndex.isUnique(); 119 if (logger.isDebugEnabled()) logger.debug(toString()); 120 } 121 122 /** Construct an IndexHandlerImpl from an index declared on a field. 123 * There may be more than one column in the index; the columns are taken from the 124 * columns mapped by the field. 125 * @param domainTypeHandler the domain type handler 126 * @param dictionary the Dictionary 127 * @param indexName the index name 128 * @param fmd the DomainFieldHandlerImpl that corresponds to the column 129 */ IndexHandlerImpl(DomainTypeHandler<?> domainTypeHandler, Dictionary dictionary, String indexName, AbstractDomainFieldHandlerImpl fmd)130 public IndexHandlerImpl(DomainTypeHandler<?> domainTypeHandler, 131 Dictionary dictionary, String indexName, AbstractDomainFieldHandlerImpl fmd) { 132 this.className = domainTypeHandler.getName(); 133 this.indexName = indexName; 134 this.tableName = domainTypeHandler.getTableName(); 135 this.storeIndex = getIndex(dictionary, tableName, indexName); 136 this.unique = isUnique(storeIndex); 137 this.columnNames = fmd.getColumnNames(); 138 this.fields = new AbstractDomainFieldHandlerImpl[]{fmd}; 139 if (logger.isDebugEnabled()) logger.debug(toString()); 140 } 141 142 /** Create a CandidateIndexImpl from this IndexHandlerImpl. 143 * The CandidateIndexImpl is used to decide on the strategy for a query. 144 * 145 */ toCandidateIndexImpl()146 public CandidateIndexImpl toCandidateIndexImpl() { 147 if (!usable) { 148 return CandidateIndexImpl.getIndexForNullWhereClause(); 149 } else { 150 return new CandidateIndexImpl( 151 className, storeIndex, unique, fields); 152 } 153 } 154 155 @Override toString()156 public String toString() { 157 StringBuffer buffer = new StringBuffer(); 158 buffer.append("IndexHandler for class "); 159 buffer.append(className); 160 buffer.append(" index: "); 161 buffer.append(indexName); 162 buffer.append(" unique: "); 163 buffer.append(unique); 164 buffer.append(" columns: "); 165 buffer.append(Arrays.toString(columnNames)); 166 return buffer.toString(); 167 } 168 169 /** Set the DomainFieldHandlerImpl once it's known. 170 * 171 * @param i the index into the fields array 172 * @param fmd the DomainFieldHandlerImpl for the corresponding column 173 */ setDomainFieldHandlerFor(int i, AbstractDomainFieldHandlerImpl fmd)174 public void setDomainFieldHandlerFor(int i, AbstractDomainFieldHandlerImpl fmd) { 175 fields[i] = fmd; 176 fmd.validateIndexType(indexName, unique); 177 } 178 179 /** Accessor for columnNames. */ getColumnNames()180 public String[] getColumnNames() { 181 return columnNames; 182 } 183 184 /** Validate that all columnNames have fields. 185 * 186 */ assertAllColumnsHaveFields()187 public void assertAllColumnsHaveFields() { 188 for (int i = 0; i < columnNames.length; ++i) { 189 AbstractDomainFieldHandlerImpl fmd = fields[i]; 190 if (fmd == null || !(columnNames[i].equals(fmd.getColumnName()))) { 191 usable = false; 192 reason = local.message( 193 "ERR_Index_Mismatch", className, indexName, columnNames[i]); 194 } 195 } 196 } 197 isUnique(Index storeIndex)198 protected boolean isUnique(Index storeIndex) { 199 return storeIndex.isUnique(); 200 } 201 isUsable()202 public boolean isUsable() { 203 return usable; 204 } 205 getReason()206 public String getReason() { 207 return reason; 208 } 209 getIndex(Dictionary dictionary, String tableName, String indexName)210 protected Index getIndex(Dictionary dictionary, 211 String tableName, String indexName) { 212 return dictionary.getIndex(indexName, tableName, indexName); 213 } 214 getIndexName()215 public String getIndexName() { 216 return indexName; 217 } 218 219 } 220