File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\index\BaseIndex.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.index;
8
9 import org.h2.constant.ErrorCode;
10 import org.h2.engine.Constants;
11 import org.h2.engine.DbObject;
12 import org.h2.engine.Mode;
13 import org.h2.engine.Session;
14 import org.h2.message.DbException;
15 import org.h2.message.Trace;
16 import org.h2.result.Row;
17 import org.h2.result.SearchRow;
18 import org.h2.result.SortOrder;
19 import org.h2.schema.SchemaObjectBase;
20 import org.h2.table.Column;
21 import org.h2.table.IndexColumn;
22 import org.h2.table.Table;
23 import org.h2.table.TableFilter;
24 import org.h2.util.MathUtils;
25 import org.h2.util.StatementBuilder;
26 import org.h2.util.StringUtils;
27 import org.h2.value.Value;
28 import org.h2.value.ValueNull;
29
30 /**
31 * Most index implementations extend the base index.
32 */
33 public abstract class BaseIndex extends SchemaObjectBase implements Index {
34
35 protected IndexColumn[] indexColumns;
36 protected Column[] columns;
37 protected int[] columnIds;
38 protected Table table;
39 protected IndexType indexType;
40 protected boolean isMultiVersion;
41
42 /**
43 * Initialize the base index.
44 *
45 * @param newTable the table
46 * @param id the object id
47 * @param name the index name
48 * @param newIndexColumns the columns that are indexed or null if this is
49 * not yet known
50 * @param newIndexType the index type
51 */
52 protected void initBaseIndex(Table newTable, int id, String name,
53 IndexColumn[] newIndexColumns, IndexType newIndexType) {
54 initSchemaObjectBase(newTable.getSchema(), id, name, Trace.INDEX);
55 this.indexType = newIndexType;
56 this.table = newTable;
57 if (newIndexColumns != null) {
58 this.indexColumns = newIndexColumns;
59 columns = new Column[newIndexColumns.length];
60 int len = columns.length;
61 columnIds = new int[len];
62 for (int i = 0; i < len; i++) {
63 Column col = newIndexColumns[i].column;
64 columns[i] = col;
65 columnIds[i] = col.getColumnId();
66 }
67 }
68 }
69
70 public String getDropSQL() {
71 return null;
72 }
73
74 /**
75 * Create a duplicate key exception with a message that contains the index name
76 *
77 * @return the exception
78 */
79 DbException getDuplicateKeyException() {
80 String sql = getName() + " ON " + table.getSQL() + "(" + getColumnListSQL() + ")";
81 DbException e = DbException.get(ErrorCode.DUPLICATE_KEY_1, sql);
82 e.setSource(this);
83 return e;
84 }
85
86 public String getPlanSQL() {
87 return getSQL();
88 }
89
90 public void removeChildrenAndResources(Session session) {
91 table.removeIndex(this);
92 remove(session);
93 database.removeMeta(session, getId());
94 }
95
96 public boolean canFindNext() {
97 return false;
98 }
99
100
101 public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
102 return find(filter.getSession(), first, last);
103 }
104
105 /**
106 * Find a row or a list of rows that is larger and create a cursor to
107 * iterate over the result. The base implementation doesn't support this feature.
108 *
109 * @param session the session
110 * @param higherThan the lower limit (excluding)
111 * @param last the last row, or null for no limit
112 * @return the cursor
113 * @throws DbException always
114 */
115 public Cursor findNext(Session session, SearchRow higherThan, SearchRow last) {
116 throw DbException.throwInternalError();
117 }
118
119 /**
120 * Calculate the cost for the given mask as if this index was a typical
121 * b-tree range index. This is the estimated cost required to search one
122 * row, and then iterate over the given number of rows.
123 *
124 * @param masks the search mask
125 * @param rowCount the number of rows in the index
126 * @return the estimated cost
127 */
128 protected long getCostRangeIndex(int[] masks, long rowCount) {
129 rowCount += Constants.COST_ROW_OFFSET;
130 long cost = rowCount;
131 long rows = rowCount;
132 int totalSelectivity = 0;
133 if (masks == null) {
134 return cost;
135 }
136 for (int i = 0, len = columns.length; i < len; i++) {
137 Column column = columns[i];
138 int index = column.getColumnId();
139 int mask = masks[index];
140 if ((mask & IndexCondition.EQUALITY) == IndexCondition.EQUALITY) {
141 if (i == columns.length - 1 && getIndexType().isUnique()) {
142 cost = 3;
143 break;
144 }
145 totalSelectivity = 100 - ((100 - totalSelectivity) * (100 - column.getSelectivity()) / 100);
146 long distinctRows = rowCount * totalSelectivity / 100;
147 if (distinctRows <= 0) {
148 distinctRows = 1;
149 }
150 rows = Math.max(rowCount / distinctRows, 1);
151 cost = 2 + rows;
152 } else if ((mask & IndexCondition.RANGE) == IndexCondition.RANGE) {
153 cost = 2 + rows / 4;
154 break;
155 } else if ((mask & IndexCondition.START) == IndexCondition.START) {
156 cost = 2 + rows / 3;
157 break;
158 } else if ((mask & IndexCondition.END) == IndexCondition.END) {
159 cost = rows / 3;
160 break;
161 } else {
162 break;
163 }
164 }
165 return cost;
166 }
167
168 public int compareRows(SearchRow rowData, SearchRow compare) {
169 if (rowData == compare) {
170 return 0;
171 }
172 for (int i = 0, len = indexColumns.length; i < len; i++) {
173 int index = columnIds[i];
174 Value v = compare.getValue(index);
175 if (v == null) {
176 // can't compare further
177 return 0;
178 }
179 int c = compareValues(rowData.getValue(index), v, indexColumns[i].sortType);
180 if (c != 0) {
181 return c;
182 }
183 }
184 return 0;
185 }
186
187 /**
188 * Check if one of the columns is NULL and multiple rows with NULL are
189 * allowed using the current compatibility mode for unique indexes. Note:
190 * NULL behavior is complicated in SQL.
191 *
192 * @param newRow the row to check
193 * @return true if one of the columns is null and multiple nulls in unique
194 * indexes are allowed
195 */
196 boolean containsNullAndAllowMultipleNull(SearchRow newRow) {
197 Mode mode = database.getMode();
198 if (mode.uniqueIndexSingleNull) {
199 return false;
200 } else if (mode.uniqueIndexSingleNullExceptAllColumnsAreNull) {
201 for (int index : columnIds) {
202 Value v = newRow.getValue(index);
203 if (v != ValueNull.INSTANCE) {
204 return false;
205 }
206 }
207 return true;
208 }
209 for (int index : columnIds) {
210 Value v = newRow.getValue(index);
211 if (v == ValueNull.INSTANCE) {
212 return true;
213 }
214 }
215 return false;
216 }
217
218 /**
219 * Compare the positions of two rows.
220 *
221 * @param rowData the first row
222 * @param compare the second row
223 * @return 0 if both rows are equal, -1 if the first row is smaller, otherwise 1
224 */
225 int compareKeys(SearchRow rowData, SearchRow compare) {
226 long k1 = rowData.getKey();
227 long k2 = compare.getKey();
228 if (k1 == k2) {
229 if (isMultiVersion) {
230 int v1 = rowData.getVersion();
231 int v2 = compare.getVersion();
232 return MathUtils.compareInt(v2, v1);
233 }
234 return 0;
235 }
236 return k1 > k2 ? 1 : -1;
237 }
238
239 private int compareValues(Value a, Value b, int sortType) {
240 if (a == b) {
241 return 0;
242 }
243 boolean aNull = a == null, bNull = b == null;
244 if (aNull || bNull) {
245 return SortOrder.compareNull(aNull, sortType);
246 }
247 int comp = table.compareTypeSave(a, b);
248 if ((sortType & SortOrder.DESCENDING) != 0) {
249 comp = -comp;
250 }
251 return comp;
252 }
253
254 public int getColumnIndex(Column col) {
255 for (int i = 0, len = columns.length; i < len; i++) {
256 if (columns[i].equals(col)) {
257 return i;
258 }
259 }
260 return -1;
261 }
262
263 /**
264 * Get the list of columns as a string.
265 *
266 * @return the list of columns
267 */
268 private String getColumnListSQL() {
269 StatementBuilder buff = new StatementBuilder();
270 for (IndexColumn c : indexColumns) {
271 buff.appendExceptFirst(", ");
272 buff.append(c.getSQL());
273 }
274 return buff.toString();
275 }
276
277 public String getCreateSQLForCopy(Table targetTable, String quotedName) {
278 StringBuilder buff = new StringBuilder("CREATE ");
279 buff.append(indexType.getSQL());
280 buff.append(' ');
281 if (table.isHidden()) {
282 buff.append("IF NOT EXISTS ");
283 }
284 buff.append(quotedName);
285 buff.append(" ON ").append(targetTable.getSQL());
286 if (comment != null) {
287 buff.append(" COMMENT ").append(StringUtils.quoteStringSQL(comment));
288 }
289 buff.append('(').append(getColumnListSQL()).append(')');
290 return buff.toString();
291 }
292
293 public String getCreateSQL() {
294 return getCreateSQLForCopy(table, getSQL());
295 }
296
297 public IndexColumn[] getIndexColumns() {
298 return indexColumns;
299 }
300
301 public Column[] getColumns() {
302 return columns;
303 }
304
305 public IndexType getIndexType() {
306 return indexType;
307 }
308
309 public int getType() {
310 return DbObject.INDEX;
311 }
312
313 public Table getTable() {
314 return table;
315 }
316
317 public void commit(int operation, Row row) {
318 // nothing to do
319 }
320
321 void setMultiVersion(boolean multiVersion) {
322 this.isMultiVersion = multiVersion;
323 }
324
325 public Row getRow(Session session, long key) {
326 throw DbException.getUnsupportedException(toString());
327 }
328
329 public boolean isHidden() {
330 return table.isHidden();
331 }
332
333 public boolean isRowIdIndex() {
334 return false;
335 }
336
337 public boolean canScan() {
338 return true;
339 }
340
341 public void setSortedInsertMode(boolean sortedInsertMode) {
342 // ignore
343 }
344
345 }
346