001: /*
002:
003: Derby - Class org.apache.derby.iapi.store.access.RowUtil
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.iapi.store.access;
023:
024: import org.apache.derby.iapi.services.monitor.Monitor;
025:
026: import org.apache.derby.iapi.services.sanity.SanityManager;
027:
028: import org.apache.derby.iapi.error.StandardException;
029: import org.apache.derby.iapi.services.io.Storable;
030: import org.apache.derby.iapi.types.DataValueDescriptor;
031: import org.apache.derby.iapi.services.io.FormatableBitSet;
032: import org.apache.derby.iapi.services.loader.InstanceGetter;
033:
034: import org.apache.derby.iapi.store.raw.FetchDescriptor;
035:
036: import java.lang.reflect.InvocationTargetException;
037:
038: import java.util.Enumeration;
039: import java.util.Hashtable;
040: import java.util.Vector;
041:
042: /**
043: A set of static utility methods to work with rows.
044: <P>
045: A row or partial row is described by two or three parameters.
046: <OL>
047: <LI>DataValueDescriptor[] row - an array of objects, one per column.
048: <LI>FormatableBitSet validColumns -
049: an indication of which objects in row map to which columns
050: </OL>
051: These objects can describe a complete row or a partial row. A partial row is
052: one where a sub-set (e.g. columns 0, 4 and 7) of the columns are supplied
053: for update, or requested to be fetched on a read. Here's an example
054: of code to set up a partial column list to fetch the 0th (type FOO),
055: 4th (type BAR), and 7th (type MMM) columns from a row with 10 columns, note
056: that the format for a partial row changed from a "packed" representation
057: in the 3.0 release to a "sparse" representation in later releases:
058:
059: <blockquote><pre>
060:
061: // allocate/initialize the row
062: DataValueDescriptor row = new DataValueDescriptor[10]
063: row[0] = new FOO();
064: row[4] = new BAR();
065: row[7] = new MMM();
066:
067: // allocate/initialize the bit set
068: FormatableBitSet FormatableBitSet = new FormatableBitSet(10);
069:
070: FormatableBitSet.set(0);
071: FormatableBitSet.set(4);
072: FormatableBitSet.set(7);
073: </blockquote></pre>
074:
075:
076: <BR><B>Column mapping<B><BR>
077: When validColumns is null:
078: <UL>
079: <LI> The number of columns is given by row.length
080: <LI> Column N maps to row[N], where column numbers start at zero.
081: </UL>
082: <BR>
083: When validColumns is not null, then
084: <UL>
085: <LI> The number of requested columns is given by the number of bits set in
086: validColumns.
087: <LI> Column N is not in the partial row if validColumns.isSet(N)
088: returns false.
089: <LI> Column N is in the partial row if validColumns.isSet(N) returns true.
090: <LI> If column N is in the partial row then it maps to row[N].
091: If N >= row.length then the column is taken as non existent for an
092: insert or update, and not fetched on a fetch.
093: </UL>
094: If row.length is greater than the number of columns indicated by validColumns
095: the extra entries are ignored.
096:
097: **/
098: public class RowUtil {
099: private RowUtil() {
100: }
101:
102: /**
103: An object that can be used on a fetch to indicate no fields
104: need to be fetched.
105: */
106: public static final DataValueDescriptor[] EMPTY_ROW = new DataValueDescriptor[0];
107:
108: /**
109: An object that can be used on a fetch as a FormatableBitSet to indicate no fields
110: need to be fetched.
111: */
112: public static final FormatableBitSet EMPTY_ROW_BITSET = new FormatableBitSet(
113: 0);
114:
115: /**
116: An object that can be used on a fetch as a FormatableBitSet to indicate no fields
117: need to be fetched.
118: */
119: public static final FetchDescriptor EMPTY_ROW_FETCH_DESCRIPTOR = new FetchDescriptor(
120: 0);
121:
122: public static final FetchDescriptor[] ROWUTIL_FETCH_DESCRIPTOR_CONSTANTS = {
123: EMPTY_ROW_FETCH_DESCRIPTOR, new FetchDescriptor(1, 1),
124: new FetchDescriptor(2, 2), new FetchDescriptor(3, 3),
125: new FetchDescriptor(4, 4), new FetchDescriptor(5, 5),
126: new FetchDescriptor(6, 6), new FetchDescriptor(7, 7) };
127:
128: /**
129: Get the object for a column identifer (0 based) from a complete or
130: partial row.
131:
132: @param row the row
133: @param columnList valid columns in the row
134: @param columnId which column to return (0 based)
135:
136: @return the obejct for the column, or null if the column is not represented.
137: */
138: public static DataValueDescriptor getColumn(
139: DataValueDescriptor[] row, FormatableBitSet columnList,
140: int columnId) {
141:
142: if (columnList == null)
143: return columnId < row.length ? row[columnId] : null;
144:
145: if (!(columnList.getLength() > columnId && columnList
146: .isSet(columnId)))
147: return null;
148:
149: return columnId < row.length ? row[columnId] : null;
150:
151: }
152:
153: public static Object getColumn(Object[] row,
154: FormatableBitSet columnList, int columnId) {
155:
156: if (columnList == null)
157: return columnId < row.length ? row[columnId] : null;
158:
159: if (!(columnList.getLength() > columnId && columnList
160: .isSet(columnId)))
161: return null;
162:
163: return columnId < row.length ? row[columnId] : null;
164:
165: }
166:
167: /**
168: Get a FormatableBitSet representing all the columns represented in
169: a qualifier list.
170:
171: @return a FormatableBitSet describing the valid columns.
172: */
173: public static FormatableBitSet getQualifierBitSet(
174: Qualifier[][] qualifiers) {
175: FormatableBitSet qualifierColumnList = new FormatableBitSet();
176:
177: if (qualifiers != null) {
178: for (int i = 0; i < qualifiers.length; i++) {
179: for (int j = 0; j < qualifiers[i].length; j++) {
180: int colId = qualifiers[i][j].getColumnId();
181:
182: // we are about to set bit colId, need length to be colId+1
183: qualifierColumnList.grow(colId + 1);
184: qualifierColumnList.set(colId);
185: }
186: }
187: }
188:
189: return qualifierColumnList;
190: }
191:
192: /**
193: * Get the number of columns represented by a FormatableBitSet.
194: * <p>
195: * This is simply a count of the number of bits set in the FormatableBitSet.
196: * <p>
197: *
198: * @param maxColumnNumber Because the FormatableBitSet.size() can't be used as
199: * the number of columns, allow caller to tell
200: * the maximum column number if it knows.
201: * -1 means caller does not know.
202: * >=0 number is the largest column number.
203: *
204: * @param columnList valid columns in the row
205: *
206: * @return The number of columns represented in the FormatableBitSet.
207: **/
208: public static int getNumberOfColumns(int maxColumnNumber,
209: FormatableBitSet columnList) {
210: if (SanityManager.DEBUG)
211: SanityManager.ASSERT(columnList != null);
212:
213: int max_col_number = columnList.getLength();
214:
215: if (maxColumnNumber > 0 && maxColumnNumber < max_col_number)
216: max_col_number = maxColumnNumber;
217:
218: int ret_num_cols = 0;
219:
220: for (int i = 0; i < max_col_number; i++) {
221: if (columnList.isSet(i))
222: ret_num_cols++;
223: }
224:
225: return (ret_num_cols);
226: }
227:
228: /**
229: See if a row actually contains no columns.
230: Returns true if row is null or row.length is zero.
231:
232: @return true if row is empty.
233: */
234: public static boolean isRowEmpty(DataValueDescriptor[] row) {
235:
236: if (row == null)
237: return true;
238:
239: if (row.length == 0)
240: return true;
241:
242: return false;
243: }
244:
245: /**
246: Return the column number of the first column out of range, or a number
247: less than zero if all columns are in range.
248: */
249: public static int columnOutOfRange(DataValueDescriptor[] row,
250: FormatableBitSet columnList, int maxColumns) {
251:
252: if (columnList == null) {
253: if (row.length > maxColumns)
254: return maxColumns;
255:
256: return -1;
257: }
258:
259: int size = columnList.getLength();
260: for (int i = maxColumns; i < size; i++) {
261: if (columnList.isSet(i))
262: return i;
263: }
264:
265: return -1;
266: }
267:
268: /**
269: Get the next valid column after or including start column.
270: Returns -1 if no valid columns exist after startColumn
271: */
272: public static int nextColumn(Object[] row,
273: FormatableBitSet columnList, int startColumn) {
274:
275: if (columnList != null) {
276:
277: int size = columnList.getLength();
278:
279: for (; startColumn < size; startColumn++) {
280: if (columnList.isSet(startColumn)) {
281: return startColumn;
282: }
283: }
284:
285: return -1;
286: }
287:
288: if (row == null)
289: return -1;
290:
291: return startColumn < row.length ? startColumn : -1;
292: }
293:
294: /**
295: * Return a FetchDescriptor which describes a single column set.
296: * <p>
297: * This routine returns one of a set of constant FetchDescriptor's, and
298: * should not be altered by the caller.
299: **/
300: public static final FetchDescriptor getFetchDescriptorConstant(
301: int single_column_number) {
302: if (single_column_number < ROWUTIL_FETCH_DESCRIPTOR_CONSTANTS.length) {
303: return (ROWUTIL_FETCH_DESCRIPTOR_CONSTANTS[single_column_number]);
304: } else {
305: return (new FetchDescriptor(single_column_number,
306: single_column_number));
307: }
308: }
309:
310: /**************************************************************************
311: * Public Methods dealing with cloning and row copying util functions
312: **************************************************************************
313: */
314:
315: /**
316: * Generate a row of InstanceGetter objects to be used to generate "empty" rows.
317: * <p>
318: * Generate an array of InstanceGetter objects which will be used to make
319: * repeated calls to newRowFromClassInfoTemplate(), to repeatedly and
320: * efficiently generate new rows. This is important for certain
321: * applications like the sorter and fetchSet which generate large numbers
322: * of "new" empty rows.
323: * <p>
324: *
325: * @return The new row.
326: *
327: * @param format_ids an array of format id's, one per column in row.
328: *
329: * @exception StandardException Standard exception policy.
330: **/
331: public static InstanceGetter[] newClassInfoTemplate(
332: FormatableBitSet column_list, int[] format_ids)
333: throws StandardException {
334: int num_cols = format_ids.length;
335: InstanceGetter[] ret_row = new InstanceGetter[num_cols];
336:
337: int column_listSize = (column_list == null) ? 0 : column_list
338: .getLength();
339:
340: for (int i = 0; i < num_cols; i++) {
341: // does caller want this column?
342: if ((column_list != null)
343: && !((column_listSize > i) && (column_list.isSet(i)))) {
344: // no - column should be skipped.
345: } else {
346: // yes - create the column
347:
348: // get empty instance of object identified by the format id.
349:
350: ret_row[i] = Monitor.classFromIdentifier(format_ids[i]);
351: }
352: }
353:
354: return (ret_row);
355: }
356:
357: private static void newRowFromClassInfoTemplateError() {
358: if (SanityManager.DEBUG)
359: SanityManager
360: .THROWASSERT("unexpected error in newRowFromClassInfoTemplate()");
361: }
362:
363: /**
364: * Generate an "empty" row from an array of classInfo objects.
365: * <p>
366: * Generate an array of new'd objects by using the getNewInstance()
367: * method on each of the InstanceGetter objects. It is more
368: * efficient to allocate new objects based on this "cache'd"
369: * InstanceGetter object than to call the Monitor to generate a new class
370: * from a format id.
371: * <p>
372: *
373: * @return The new row.
374: *
375: * @param classinfo_template An array of InstanceGetter objects each of
376: * which can be used to create a new instance
377: * of the appropriate type to build a new empty
378: * template row.
379: *
380: * @exception StandardException Standard exception policy.
381: **/
382: public static DataValueDescriptor[] newRowFromClassInfoTemplate(
383: InstanceGetter[] classinfo_template)
384: throws StandardException {
385:
386: DataValueDescriptor[] columns = new DataValueDescriptor[classinfo_template.length];
387:
388: try {
389: for (int column_index = classinfo_template.length; column_index-- > 0;) {
390: if (classinfo_template[column_index] != null) {
391: // get empty instance of DataValueDescriptor identified by
392: // the format id.
393: columns[column_index] = (DataValueDescriptor) classinfo_template[column_index]
394: .getNewInstance();
395: }
396: }
397: } catch (InstantiationException ie) {
398: newRowFromClassInfoTemplateError();
399: } catch (IllegalAccessException iae) {
400: newRowFromClassInfoTemplateError();
401: } catch (InvocationTargetException ite) {
402: newRowFromClassInfoTemplateError();
403: }
404:
405: return columns;
406: }
407:
408: /**
409: * return string version of row.
410: * <p>
411: * For debugging only.
412: *
413: * @return The string version of row.
414: *
415: * @param row The row.
416: *
417: **/
418: public static String toString(Object[] row) {
419: if (SanityManager.DEBUG) {
420:
421: String str = new String();
422:
423: if (row != null) {
424: if (row.length == 0) {
425: str = "empty row";
426: } else {
427: for (int i = 0; i < row.length; i++)
428: str += "col[" + i + "]=" + row[i];
429: }
430: } else {
431: str = "row is null";
432: }
433:
434: return (str);
435: } else {
436: return (null);
437: }
438: }
439:
440: /**
441: * return string version of a HashTable returned from a FetchSet.
442: * <p>
443: *
444: * @return The string version of row.
445: *
446: *
447: **/
448:
449: // For debugging only.
450: public static String toString(Hashtable hash_table) {
451: if (SanityManager.DEBUG) {
452: String str = new String();
453:
454: Object row_or_vector;
455:
456: for (Enumeration e = hash_table.elements(); e
457: .hasMoreElements();) {
458: row_or_vector = e.nextElement();
459:
460: if (row_or_vector instanceof Object[]) {
461: // it's a row
462: str += RowUtil.toString((Object[]) row_or_vector);
463: str += "\n";
464: } else if (row_or_vector instanceof Vector) {
465: // it's a vector
466: Vector vec = (Vector) row_or_vector;
467:
468: for (int i = 0; i < vec.size(); i++) {
469: str += "vec["
470: + i
471: + "]:"
472: + RowUtil.toString((Object[]) vec
473: .elementAt(i));
474:
475: str += "\n";
476: }
477: } else {
478: str += "BAD ENTRY\n";
479: }
480: }
481: return (str);
482: } else {
483: return (null);
484: }
485: }
486:
487: /**
488: * Process the qualifier list on the row, return true if it qualifies.
489: * <p>
490: * A two dimensional array is to be used to pass around a AND's and OR's in
491: * conjunctive normal form. The top slot of the 2 dimensional array is
492: * optimized for the more frequent where no OR's are present. The first
493: * array slot is always a list of AND's to be treated as described above
494: * for single dimensional AND qualifier arrays. The subsequent slots are
495: * to be treated as AND'd arrays or OR's. Thus the 2 dimensional array
496: * qual[][] argument is to be treated as the following, note if
497: * qual.length = 1 then only the first array is valid and it is and an
498: * array of and clauses:
499: *
500: * (qual[0][0] and qual[0][0] ... and qual[0][qual[0].length - 1])
501: * and
502: * (qual[1][0] or qual[1][1] ... or qual[1][qual[1].length - 1])
503: * and
504: * (qual[2][0] or qual[2][1] ... or qual[2][qual[2].length - 1])
505: * ...
506: * and
507: * (qual[qual.length - 1][0] or qual[1][1] ... or qual[1][2])
508: *
509: *
510: * @return true if the row qualifies.
511: *
512: * @param row The row being qualified.
513: * @param qual_list 2 dimensional array representing conjunctive
514: * normal form of simple qualifiers.
515: *
516: * @exception StandardException Standard exception policy.
517: **/
518: public static final boolean qualifyRow(Object[] row,
519: Qualifier[][] qual_list) throws StandardException {
520: boolean row_qualifies = true;
521:
522: if (SanityManager.DEBUG) {
523: SanityManager.ASSERT(row != null);
524: }
525:
526: // First do the qual[0] which is an array of qualifer terms.
527:
528: if (SanityManager.DEBUG) {
529: // routine should not be called if there is no qualifier
530: SanityManager.ASSERT(qual_list != null);
531: SanityManager.ASSERT(qual_list.length > 0);
532: }
533:
534: for (int i = 0; i < qual_list[0].length; i++) {
535: // process each AND clause
536:
537: row_qualifies = false;
538:
539: // process each OR clause.
540:
541: Qualifier q = qual_list[0][i];
542:
543: // Get the column from the possibly partial row, of the
544: // q.getColumnId()'th column in the full row.
545: DataValueDescriptor columnValue = (DataValueDescriptor) row[q
546: .getColumnId()];
547:
548: row_qualifies = columnValue.compare(q.getOperator(), q
549: .getOrderable(), q.getOrderedNulls(), q
550: .getUnknownRV());
551:
552: if (q.negateCompareResult())
553: row_qualifies = !row_qualifies;
554:
555: // Once an AND fails the whole Qualification fails - do a return!
556: if (!row_qualifies)
557: return (false);
558: }
559:
560: // all the qual[0] and terms passed, now process the OR clauses
561:
562: for (int and_idx = 1; and_idx < qual_list.length; and_idx++) {
563: // loop through each of the "and" clause.
564:
565: row_qualifies = false;
566:
567: if (SanityManager.DEBUG) {
568: // Each OR clause must be non-empty.
569: SanityManager.ASSERT(qual_list[and_idx].length > 0);
570: }
571:
572: for (int or_idx = 0; or_idx < qual_list[and_idx].length; or_idx++) {
573: // Apply one qualifier to the row.
574: Qualifier q = qual_list[and_idx][or_idx];
575: int col_id = q.getColumnId();
576:
577: if (SanityManager.DEBUG) {
578: SanityManager
579: .ASSERT((col_id < row.length),
580: "Qualifier is referencing a column not in the row.");
581: }
582:
583: // Get the column from the possibly partial row, of the
584: // q.getColumnId()'th column in the full row.
585: DataValueDescriptor columnValue = (DataValueDescriptor) row[q
586: .getColumnId()];
587:
588: if (SanityManager.DEBUG) {
589: if (columnValue == null)
590: SanityManager.THROWASSERT("1:row = "
591: + RowUtil.toString(row)
592: + "row.length = " + row.length
593: + ";q.getColumnId() = "
594: + q.getColumnId());
595: }
596:
597: // do the compare between the column value and value in the
598: // qualifier.
599: row_qualifies = columnValue.compare(q.getOperator(), q
600: .getOrderable(), q.getOrderedNulls(), q
601: .getUnknownRV());
602:
603: if (q.negateCompareResult())
604: row_qualifies = !row_qualifies;
605:
606: // SanityManager.DEBUG_PRINT("StoredPage.qual", "processing qual[" + and_idx + "][" + or_idx + "] = " + qual_list[and_idx][or_idx] );
607:
608: // SanityManager.DEBUG_PRINT("StoredPage.qual", "value = " + row_qualifies);
609:
610: // processing "OR" clauses, so as soon as one is true, break
611: // to go and process next AND clause.
612: if (row_qualifies)
613: break;
614:
615: }
616:
617: // The qualifier list represented a set of "AND'd"
618: // qualifications so as soon as one is false processing is done.
619: if (!row_qualifies)
620: break;
621: }
622:
623: return (row_qualifies);
624: }
625:
626: }
|