Source code: org/apache/derby/iapi/store/access/RowUtil.java
1 /*
2
3 Derby - Class org.apache.derby.iapi.store.access.RowUtil
4
5 Copyright 1998, 2004 The Apache Software Foundation or its licensors, as applicable.
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18
19 */
20
21 package org.apache.derby.iapi.store.access;
22
23 import org.apache.derby.iapi.services.monitor.Monitor;
24
25 import org.apache.derby.iapi.services.sanity.SanityManager;
26
27 import org.apache.derby.iapi.error.StandardException;
28 import org.apache.derby.iapi.services.io.Storable;
29 import org.apache.derby.iapi.types.DataValueDescriptor;
30 import org.apache.derby.iapi.services.io.FormatableBitSet;
31 import org.apache.derby.iapi.services.loader.InstanceGetter;
32
33 import org.apache.derby.iapi.store.raw.FetchDescriptor;
34
35 import java.lang.reflect.InvocationTargetException;
36
37 import java.util.Enumeration;
38 import java.util.Hashtable;
39 import java.util.Vector;
40
41 /**
42 A set of static utility methods to work with rows.
43 <P>
44 A row or partial row is described by two or three parameters.
45 <OL>
46 <LI>DataValueDescriptor[] row - an array of objects, one per column.
47 <LI>FormatableBitSet validColumns -
48 an indication of which objects in row map to which columns
49 </OL>
50 These objects can describe a complete row or a partial row. A partial row is
51 one where a sub-set (e.g. columns 0, 4 and 7) of the columns are supplied
52 for update, or requested to be fetched on a read. Here's an example
53 of code to set up a partial column list to fetch the 0th (type FOO),
54 4th (type BAR), and 7th (type MMM) columns from a row with 10 columns, note
55 that the format for a partial row changed from a "packed" representation
56 in the 3.0 release to a "sparse" representation in later releases:
57
58 <blockquote><pre>
59
60 // allocate/initialize the row
61 DataValueDescriptor row = new DataValueDescriptor[10]
62 row[0] = new FOO();
63 row[4] = new BAR();
64 row[7] = new MMM();
65
66 // allocate/initialize the bit set
67 FormatableBitSet FormatableBitSet = new FormatableBitSet(10);
68
69 FormatableBitSet.set(0);
70 FormatableBitSet.set(4);
71 FormatableBitSet.set(7);
72 </blockquote></pre>
73
74
75 <BR><B>Column mapping<B><BR>
76 When validColumns is null:
77 <UL>
78 <LI> The number of columns is given by row.length
79 <LI> Column N maps to row[N], where column numbers start at zero.
80 </UL>
81 <BR>
82 When validColumns is not null, then
83 <UL>
84 <LI> The number of requested columns is given by the number of bits set in
85 validColumns.
86 <LI> Column N is not in the partial row if validColumns.isSet(N)
87 returns false.
88 <LI> Column N is in the partial row if validColumns.isSet(N) returns true.
89 <LI> If column N is in the partial row then it maps to row[N].
90 If N >= row.length then the column is taken as non existent for an
91 insert or update, and not fetched on a fetch.
92 </UL>
93 If row.length is greater than the number of columns indicated by validColumns
94 the extra entries are ignored.
95
96 **/
97 public class RowUtil
98 {
99 private RowUtil() {}
100
101 /**
102 An object that can be used on a fetch to indicate no fields
103 need to be fetched.
104 */
105 public static final DataValueDescriptor[] EMPTY_ROW =
106 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 =
113 new FormatableBitSet(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 =
120 new FetchDescriptor(0);
121
122 public static final FetchDescriptor[] ROWUTIL_FETCH_DESCRIPTOR_CONSTANTS =
123 {EMPTY_ROW_FETCH_DESCRIPTOR,
124 new FetchDescriptor(1, 1),
125 new FetchDescriptor(2, 2),
126 new FetchDescriptor(3, 3),
127 new FetchDescriptor(4, 4),
128 new FetchDescriptor(5, 5),
129 new FetchDescriptor(6, 6),
130 new FetchDescriptor(7, 7)};
131
132
133 /**
134 Get the object for a column identifer (0 based) from a complete or
135 partial row.
136
137 @param row the row
138 @param columnList valid columns in the row
139 @param columnId which column to return (0 based)
140
141 @return the obejct for the column, or null if the column is not represented.
142 */
143 public static DataValueDescriptor getColumn(
144 DataValueDescriptor[] row,
145 FormatableBitSet columnList,
146 int columnId)
147 {
148
149 if (columnList == null)
150 return columnId < row.length ? row[columnId] : null;
151
152
153 if (!(columnList.getLength() > columnId && columnList.isSet(columnId)))
154 return null;
155
156 return columnId < row.length ? row[columnId] : null;
157
158 }
159
160 public static Object getColumn(
161 Object[] row,
162 FormatableBitSet columnList,
163 int columnId)
164 {
165
166 if (columnList == null)
167 return columnId < row.length ? row[columnId] : null;
168
169
170 if (!(columnList.getLength() > columnId && columnList.isSet(columnId)))
171 return null;
172
173 return columnId < row.length ? row[columnId] : null;
174
175 }
176
177 /**
178 Get a FormatableBitSet representing all the columns represented in
179 a qualifier list.
180
181 @return a FormatableBitSet describing the valid columns.
182 */
183 public static FormatableBitSet getQualifierBitSet(Qualifier[][] qualifiers)
184 {
185 FormatableBitSet qualifierColumnList = new FormatableBitSet();
186
187 if (qualifiers != null)
188 {
189 for (int i = 0; i < qualifiers.length; i++)
190 {
191 for (int j = 0; j < qualifiers[i].length; j++)
192 {
193 int colId = qualifiers[i][j].getColumnId();
194
195 // we are about to set bit colId, need length to be colId+1
196 qualifierColumnList.grow(colId+1);
197 qualifierColumnList.set(colId);
198 }
199 }
200 }
201
202 return qualifierColumnList;
203 }
204
205 /**
206 * Get the number of columns represented by a FormatableBitSet.
207 * <p>
208 * This is simply a count of the number of bits set in the FormatableBitSet.
209 * <p>
210 *
211 * @param maxColumnNumber Because the FormatableBitSet.size() can't be used as
212 * the number of columns, allow caller to tell
213 * the maximum column number if it knows.
214 * -1 means caller does not know.
215 * >=0 number is the largest column number.
216 *
217 * @param columnList valid columns in the row
218 *
219 * @return The number of columns represented in the FormatableBitSet.
220 **/
221 public static int getNumberOfColumns(
222 int maxColumnNumber,
223 FormatableBitSet columnList)
224 {
225 if (SanityManager.DEBUG)
226 SanityManager.ASSERT(columnList != null);
227
228 int max_col_number = columnList.getLength();
229
230 if (maxColumnNumber > 0 && maxColumnNumber < max_col_number)
231 max_col_number = maxColumnNumber;
232
233 int ret_num_cols = 0;
234
235 for (int i = 0; i < max_col_number; i++)
236 {
237 if (columnList.isSet(i))
238 ret_num_cols++;
239 }
240
241 return(ret_num_cols);
242 }
243
244 /**
245 See if a row actually contains no columns.
246 Returns true if row is null, row.length is null,
247 or columnList is not null but has not bits set.
248
249 @return true if no columns are selected in this row.
250 */
251 public static boolean isRowEmpty(
252 DataValueDescriptor[] row,
253 FormatableBitSet columnList)
254 {
255
256 if (row == null)
257 return true;
258
259 if (row.length == 0)
260 return true;
261
262 if (columnList == null)
263 return false;
264
265 int size = columnList.getLength();
266 for (int i = 0; i < size; i--) {
267 if (columnList.isSet(i))
268 return true;
269 }
270 return false;
271 }
272
273 /**
274 Return the column number of the first column out of range, or a number
275 less than zero if all columns are in range.
276 */
277 public static int columnOutOfRange(
278 DataValueDescriptor[] row,
279 FormatableBitSet columnList,
280 int maxColumns)
281 {
282
283 if (columnList == null) {
284 if (row.length > maxColumns)
285 return maxColumns;
286
287 return -1;
288 }
289
290 int size = columnList.getLength();
291 for (int i = maxColumns; i < size; i++) {
292 if (columnList.isSet(i))
293 return i;
294 }
295
296 return -1;
297 }
298
299 /**
300 Get the next valid column after or including start column.
301 Returns -1 if no valid columns exist after startColumn
302 */
303 public static int nextColumn(
304 Object[] row,
305 FormatableBitSet columnList,
306 int startColumn)
307 {
308
309 if (columnList != null) {
310
311 int size = columnList.getLength();
312
313 for (; startColumn < size; startColumn++) {
314 if (columnList.isSet(startColumn)) {
315 return startColumn;
316 }
317 }
318
319 return -1;
320 }
321
322 if (row == null)
323 return -1;
324
325 return startColumn < row.length ? startColumn : -1;
326 }
327
328 /**
329 * Return a FetchDescriptor which describes a single column set.
330 * <p>
331 * This routine returns one of a set of constant FetchDescriptor's, and
332 * should not be altered by the caller.
333 **/
334 public static final FetchDescriptor getFetchDescriptorConstant(
335 int single_column_number)
336 {
337 if (single_column_number < ROWUTIL_FETCH_DESCRIPTOR_CONSTANTS.length)
338 {
339 return(ROWUTIL_FETCH_DESCRIPTOR_CONSTANTS[single_column_number]);
340 }
341 else
342 {
343 return(
344 new FetchDescriptor(
345 single_column_number, single_column_number));
346 }
347 }
348
349 /**************************************************************************
350 * Public Methods dealing with cloning and row copying util functions
351 **************************************************************************
352 */
353
354 /**
355 * Generate a row of InstanceGetter objects to be used to generate "empty" rows.
356 * <p>
357 * Generate an array of InstanceGetter objects which will be used to make
358 * repeated calls to newRowFromClassInfoTemplate(), to repeatedly and
359 * efficiently generate new rows. This is important for certain
360 * applications like the sorter and fetchSet which generate large numbers
361 * of "new" empty rows.
362 * <p>
363 *
364 * @return The new row.
365 *
366 * @param format_ids an array of format id's, one per column in row.
367 *
368 * @exception StandardException Standard exception policy.
369 **/
370 public static InstanceGetter[] newClassInfoTemplate(
371 FormatableBitSet column_list,
372 int[] format_ids)
373 throws StandardException
374 {
375 int num_cols = format_ids.length;
376 InstanceGetter[] ret_row = new InstanceGetter[num_cols];
377
378 int column_listSize =
379 (column_list == null) ? 0 : column_list.getLength();
380
381 for (int i = 0; i < num_cols; i++)
382 {
383 // does caller want this column?
384 if ((column_list != null) &&
385 !((column_listSize > i) &&
386 (column_list.isSet(i))))
387 {
388 // no - column should be skipped.
389 }
390 else
391 {
392 // yes - create the column
393
394 // get empty instance of object identified by the format id.
395
396 ret_row[i] = Monitor.classFromIdentifier(format_ids[i]);
397 }
398 }
399
400 return(ret_row);
401 }
402
403
404 private static void newRowFromClassInfoTemplateError()
405 {
406 if (SanityManager.DEBUG)
407 SanityManager.THROWASSERT(
408 "unexpected error in newRowFromClassInfoTemplate()");
409 }
410
411 /**
412 * Generate an "empty" row from an array of classInfo objects.
413 * <p>
414 * Generate an array of new'd objects by using the getNewInstance()
415 * method on each of the InstanceGetter objects. It is more
416 * efficient to allocate new objects based on this "cache'd"
417 * InstanceGetter object than to call the Monitor to generate a new class
418 * from a format id.
419 * <p>
420 *
421 * @return The new row.
422 *
423 * @param classinfo_template An array of InstanceGetter objects each of
424 * which can be used to create a new instance
425 * of the appropriate type to build a new empty
426 * template row.
427 *
428 * @exception StandardException Standard exception policy.
429 **/
430 public static DataValueDescriptor[] newRowFromClassInfoTemplate(
431 InstanceGetter[] classinfo_template)
432 throws StandardException
433 {
434
435 DataValueDescriptor[] columns =
436 new DataValueDescriptor[classinfo_template.length];
437
438 try
439 {
440 for (int column_index = classinfo_template.length;
441 column_index-- > 0;)
442 {
443 if (classinfo_template[column_index] != null)
444 {
445 // get empty instance of DataValueDescriptor identified by
446 // the format id.
447 columns[column_index] = (DataValueDescriptor)
448 classinfo_template[column_index].getNewInstance();
449 }
450 }
451 }
452 catch (InstantiationException ie)
453 {
454 newRowFromClassInfoTemplateError();
455 }
456 catch (IllegalAccessException iae)
457 {
458 newRowFromClassInfoTemplateError();
459 }
460 catch (InvocationTargetException ite)
461 {
462 newRowFromClassInfoTemplateError();
463 }
464
465 return columns;
466 }
467
468
469 /**
470 * return string version of row.
471 * <p>
472 * For debugging only.
473 *
474 * @return The string version of row.
475 *
476 * @param row The row.
477 *
478 **/
479 public static String toString(Object[] row)
480 {
481 if (SanityManager.DEBUG)
482 {
483
484 String str = new String();
485
486 if (row != null)
487 {
488 if (row.length == 0)
489 {
490 str = "empty row";
491 }
492 else
493 {
494 for (int i = 0; i < row.length; i++)
495 str += "col[" + i + "]=" + row[i];
496 }
497 }
498 else
499 {
500 str = "row is null";
501 }
502
503 return(str);
504 }
505 else
506 {
507 return(null);
508 }
509 }
510
511 /**
512 * return string version of a HashTable returned from a FetchSet.
513 * <p>
514 *
515 * @return The string version of row.
516 *
517 *
518 **/
519
520 // For debugging only.
521 public static String toString(Hashtable hash_table)
522 {
523 if (SanityManager.DEBUG)
524 {
525 String str = new String();
526
527 Object row_or_vector;
528
529 for (Enumeration e = hash_table.elements(); e.hasMoreElements();)
530 {
531 row_or_vector = e.nextElement();
532
533 if (row_or_vector instanceof Object[])
534 {
535 // it's a row
536 str += RowUtil.toString((Object[]) row_or_vector);
537 str += "\n";
538 }
539 else if (row_or_vector instanceof Vector)
540 {
541 // it's a vector
542 Vector vec = (Vector) row_or_vector;
543
544 for (int i = 0; i < vec.size(); i++)
545 {
546 str +=
547 "vec[" + i + "]:" +
548 RowUtil.toString((Object[]) vec.elementAt(i));
549
550 str += "\n";
551 }
552 }
553 else
554 {
555 str += "BAD ENTRY\n";
556 }
557 }
558 return(str);
559 }
560 else
561 {
562 return(null);
563 }
564 }
565
566 /**
567 * Process the qualifier list on the row, return true if it qualifies.
568 * <p>
569 * A two dimensional array is to be used to pass around a AND's and OR's in
570 * conjunctive normal form. The top slot of the 2 dimensional array is
571 * optimized for the more frequent where no OR's are present. The first
572 * array slot is always a list of AND's to be treated as described above
573 * for single dimensional AND qualifier arrays. The subsequent slots are
574 * to be treated as AND'd arrays or OR's. Thus the 2 dimensional array
575 * qual[][] argument is to be treated as the following, note if
576 * qual.length = 1 then only the first array is valid and it is and an
577 * array of and clauses:
578 *
579 * (qual[0][0] and qual[0][0] ... and qual[0][qual[0].length - 1])
580 * and
581 * (qual[1][0] or qual[1][1] ... or qual[1][qual[1].length - 1])
582 * and
583 * (qual[2][0] or qual[2][1] ... or qual[2][qual[2].length - 1])
584 * ...
585 * and
586 * (qual[qual.length - 1][0] or qual[1][1] ... or qual[1][2])
587 *
588 *
589 * @return true if the row qualifies.
590 *
591 * @param row The row being qualified.
592 * @param qual_list 2 dimensional array representing conjunctive
593 * normal form of simple qualifiers.
594 *
595 * @exception StandardException Standard exception policy.
596 **/
597 public static final boolean qualifyRow(
598 Object[] row,
599 Qualifier[][] qual_list)
600 throws StandardException
601 {
602 boolean row_qualifies = true;
603
604 if (SanityManager.DEBUG)
605 {
606 SanityManager.ASSERT(row != null);
607 }
608
609 // First do the qual[0] which is an array of qualifer terms.
610
611 if (SanityManager.DEBUG)
612 {
613 // routine should not be called if there is no qualifier
614 SanityManager.ASSERT(qual_list != null);
615 SanityManager.ASSERT(qual_list.length > 0);
616 }
617
618 for (int i = 0; i < qual_list[0].length; i++)
619 {
620 // process each AND clause
621
622 row_qualifies = false;
623
624 // process each OR clause.
625
626 Qualifier q = qual_list[0][i];
627
628 // Get the column from the possibly partial row, of the
629 // q.getColumnId()'th column in the full row.
630 DataValueDescriptor columnValue =
631 (DataValueDescriptor) row[q.getColumnId()];
632
633 row_qualifies =
634 columnValue.compare(
635 q.getOperator(),
636 q.getOrderable(),
637 q.getOrderedNulls(),
638 q.getUnknownRV());
639
640 if (q.negateCompareResult())
641 row_qualifies = !row_qualifies;
642
643 // Once an AND fails the whole Qualification fails - do a return!
644 if (!row_qualifies)
645 return(false);
646 }
647
648 // all the qual[0] and terms passed, now process the OR clauses
649
650 for (int and_idx = 1; and_idx < qual_list.length; and_idx++)
651 {
652 // loop through each of the "and" clause.
653
654 row_qualifies = false;
655
656 if (SanityManager.DEBUG)
657 {
658 // Each OR clause must be non-empty.
659 SanityManager.ASSERT(qual_list[and_idx].length > 0);
660 }
661
662 for (int or_idx = 0; or_idx < qual_list[and_idx].length; or_idx++)
663 {
664 // Apply one qualifier to the row.
665 Qualifier q = qual_list[and_idx][or_idx];
666 int col_id = q.getColumnId();
667
668 if (SanityManager.DEBUG)
669 {
670 SanityManager.ASSERT(
671 (col_id < row.length),
672 "Qualifier is referencing a column not in the row.");
673 }
674
675 // Get the column from the possibly partial row, of the
676 // q.getColumnId()'th column in the full row.
677 DataValueDescriptor columnValue =
678 (DataValueDescriptor) row[q.getColumnId()];
679
680 if (SanityManager.DEBUG)
681 {
682 if (columnValue == null)
683 SanityManager.THROWASSERT(
684 "1:row = " + RowUtil.toString(row) +
685 "row.length = " + row.length +
686 ";q.getColumnId() = " + q.getColumnId());
687 }
688
689 // do the compare between the column value and value in the
690 // qualifier.
691 row_qualifies =
692 columnValue.compare(
693 q.getOperator(),
694 q.getOrderable(),
695 q.getOrderedNulls(),
696 q.getUnknownRV());
697
698 if (q.negateCompareResult())
699 row_qualifies = !row_qualifies;
700
701 // SanityManager.DEBUG_PRINT("StoredPage.qual", "processing qual[" + and_idx + "][" + or_idx + "] = " + qual_list[and_idx][or_idx] );
702
703 // SanityManager.DEBUG_PRINT("StoredPage.qual", "value = " + row_qualifies);
704
705 // processing "OR" clauses, so as soon as one is true, break
706 // to go and process next AND clause.
707 if (row_qualifies)
708 break;
709
710 }
711
712 // The qualifier list represented a set of "AND'd"
713 // qualifications so as soon as one is false processing is done.
714 if (!row_qualifies)
715 break;
716 }
717
718 return(row_qualifies);
719 }
720
721 }