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