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 or row.length is zero. |
247 | |
248 | @return true if row is empty. |
249 | */ |
250 | public static boolean isRowEmpty( |
251 | DataValueDescriptor[] row) |
252 | { |
253 | |
254 | if (row == null) |
255 | return true; |
256 | |
257 | if (row.length == 0) |
258 | return true; |
259 | |
260 | return false; |
261 | } |
262 | |
263 | /** |
264 | Return the column number of the first column out of range, or a number |
265 | less than zero if all columns are in range. |
266 | */ |
267 | public static int columnOutOfRange( |
268 | DataValueDescriptor[] row, |
269 | FormatableBitSet columnList, |
270 | int maxColumns) |
271 | { |
272 | |
273 | if (columnList == null) { |
274 | if (row.length > maxColumns) |
275 | return maxColumns; |
276 | |
277 | return -1; |
278 | } |
279 | |
280 | int size = columnList.getLength(); |
281 | for (int i = maxColumns; i < size; i++) { |
282 | if (columnList.isSet(i)) |
283 | return i; |
284 | } |
285 | |
286 | return -1; |
287 | } |
288 | |
289 | /** |
290 | Get the next valid column after or including start column. |
291 | Returns -1 if no valid columns exist after startColumn |
292 | */ |
293 | public static int nextColumn( |
294 | Object[] row, |
295 | FormatableBitSet columnList, |
296 | int startColumn) |
297 | { |
298 | |
299 | if (columnList != null) { |
300 | |
301 | int size = columnList.getLength(); |
302 | |
303 | for (; startColumn < size; startColumn++) { |
304 | if (columnList.isSet(startColumn)) { |
305 | return startColumn; |
306 | } |
307 | } |
308 | |
309 | return -1; |
310 | } |
311 | |
312 | if (row == null) |
313 | return -1; |
314 | |
315 | return startColumn < row.length ? startColumn : -1; |
316 | } |
317 | |
318 | /** |
319 | * Return a FetchDescriptor which describes a single column set. |
320 | * <p> |
321 | * This routine returns one of a set of constant FetchDescriptor's, and |
322 | * should not be altered by the caller. |
323 | **/ |
324 | public static final FetchDescriptor getFetchDescriptorConstant( |
325 | int single_column_number) |
326 | { |
327 | if (single_column_number < ROWUTIL_FETCH_DESCRIPTOR_CONSTANTS.length) |
328 | { |
329 | return(ROWUTIL_FETCH_DESCRIPTOR_CONSTANTS[single_column_number]); |
330 | } |
331 | else |
332 | { |
333 | return( |
334 | new FetchDescriptor( |
335 | single_column_number, single_column_number)); |
336 | } |
337 | } |
338 | |
339 | /************************************************************************** |
340 | * Public Methods dealing with cloning and row copying util functions |
341 | ************************************************************************** |
342 | */ |
343 | |
344 | /** |
345 | * Generate a row of InstanceGetter objects to be used to generate "empty" rows. |
346 | * <p> |
347 | * Generate an array of InstanceGetter objects which will be used to make |
348 | * repeated calls to newRowFromClassInfoTemplate(), to repeatedly and |
349 | * efficiently generate new rows. This is important for certain |
350 | * applications like the sorter and fetchSet which generate large numbers |
351 | * of "new" empty rows. |
352 | * <p> |
353 | * |
354 | * @return The new row. |
355 | * |
356 | * @param format_ids an array of format id's, one per column in row. |
357 | * |
358 | * @exception StandardException Standard exception policy. |
359 | **/ |
360 | public static InstanceGetter[] newClassInfoTemplate( |
361 | FormatableBitSet column_list, |
362 | int[] format_ids) |
363 | throws StandardException |
364 | { |
365 | int num_cols = format_ids.length; |
366 | InstanceGetter[] ret_row = new InstanceGetter[num_cols]; |
367 | |
368 | int column_listSize = |
369 | (column_list == null) ? 0 : column_list.getLength(); |
370 | |
371 | for (int i = 0; i < num_cols; i++) |
372 | { |
373 | // does caller want this column? |
374 | if ((column_list != null) && |
375 | !((column_listSize > i) && |
376 | (column_list.isSet(i)))) |
377 | { |
378 | // no - column should be skipped. |
379 | } |
380 | else |
381 | { |
382 | // yes - create the column |
383 | |
384 | // get empty instance of object identified by the format id. |
385 | |
386 | ret_row[i] = Monitor.classFromIdentifier(format_ids[i]); |
387 | } |
388 | } |
389 | |
390 | return(ret_row); |
391 | } |
392 | |
393 | |
394 | private static void newRowFromClassInfoTemplateError() |
395 | { |
396 | if (SanityManager.DEBUG) |
397 | SanityManager.THROWASSERT( |
398 | "unexpected error in newRowFromClassInfoTemplate()"); |
399 | } |
400 | |
401 | /** |
402 | * Generate an "empty" row from an array of classInfo objects. |
403 | * <p> |
404 | * Generate an array of new'd objects by using the getNewInstance() |
405 | * method on each of the InstanceGetter objects. It is more |
406 | * efficient to allocate new objects based on this "cache'd" |
407 | * InstanceGetter object than to call the Monitor to generate a new class |
408 | * from a format id. |
409 | * <p> |
410 | * |
411 | * @return The new row. |
412 | * |
413 | * @param classinfo_template An array of InstanceGetter objects each of |
414 | * which can be used to create a new instance |
415 | * of the appropriate type to build a new empty |
416 | * template row. |
417 | * |
418 | * @exception StandardException Standard exception policy. |
419 | **/ |
420 | public static DataValueDescriptor[] newRowFromClassInfoTemplate( |
421 | InstanceGetter[] classinfo_template) |
422 | throws StandardException |
423 | { |
424 | |
425 | DataValueDescriptor[] columns = |
426 | new DataValueDescriptor[classinfo_template.length]; |
427 | |
428 | try |
429 | { |
430 | for (int column_index = classinfo_template.length; |
431 | column_index-- > 0;) |
432 | { |
433 | if (classinfo_template[column_index] != null) |
434 | { |
435 | // get empty instance of DataValueDescriptor identified by |
436 | // the format id. |
437 | columns[column_index] = (DataValueDescriptor) |
438 | classinfo_template[column_index].getNewInstance(); |
439 | } |
440 | } |
441 | } |
442 | catch (InstantiationException ie) |
443 | { |
444 | newRowFromClassInfoTemplateError(); |
445 | } |
446 | catch (IllegalAccessException iae) |
447 | { |
448 | newRowFromClassInfoTemplateError(); |
449 | } |
450 | catch (InvocationTargetException ite) |
451 | { |
452 | newRowFromClassInfoTemplateError(); |
453 | } |
454 | |
455 | return columns; |
456 | } |
457 | |
458 | |
459 | /** |
460 | * return string version of row. |
461 | * <p> |
462 | * For debugging only. |
463 | * |
464 | * @return The string version of row. |
465 | * |
466 | * @param row The row. |
467 | * |
468 | **/ |
469 | public static String toString(Object[] row) |
470 | { |
471 | if (SanityManager.DEBUG) |
472 | { |
473 | |
474 | String str = new String(); |
475 | |
476 | if (row != null) |
477 | { |
478 | if (row.length == 0) |
479 | { |
480 | str = "empty row"; |
481 | } |
482 | else |
483 | { |
484 | for (int i = 0; i < row.length; i++) |
485 | str += "col[" + i + "]=" + row[i]; |
486 | } |
487 | } |
488 | else |
489 | { |
490 | str = "row is null"; |
491 | } |
492 | |
493 | return(str); |
494 | } |
495 | else |
496 | { |
497 | return(null); |
498 | } |
499 | } |
500 | |
501 | /** |
502 | * return string version of a HashTable returned from a FetchSet. |
503 | * <p> |
504 | * |
505 | * @return The string version of row. |
506 | * |
507 | * |
508 | **/ |
509 | |
510 | // For debugging only. |
511 | public static String toString(Hashtable hash_table) |
512 | { |
513 | if (SanityManager.DEBUG) |
514 | { |
515 | String str = new String(); |
516 | |
517 | Object row_or_vector; |
518 | |
519 | for (Enumeration e = hash_table.elements(); e.hasMoreElements();) |
520 | { |
521 | row_or_vector = e.nextElement(); |
522 | |
523 | if (row_or_vector instanceof Object[]) |
524 | { |
525 | // it's a row |
526 | str += RowUtil.toString((Object[]) row_or_vector); |
527 | str += "\n"; |
528 | } |
529 | else if (row_or_vector instanceof Vector) |
530 | { |
531 | // it's a vector |
532 | Vector vec = (Vector) row_or_vector; |
533 | |
534 | for (int i = 0; i < vec.size(); i++) |
535 | { |
536 | str += |
537 | "vec[" + i + "]:" + |
538 | RowUtil.toString((Object[]) vec.elementAt(i)); |
539 | |
540 | str += "\n"; |
541 | } |
542 | } |
543 | else |
544 | { |
545 | str += "BAD ENTRY\n"; |
546 | } |
547 | } |
548 | return(str); |
549 | } |
550 | else |
551 | { |
552 | return(null); |
553 | } |
554 | } |
555 | |
556 | /** |
557 | * Process the qualifier list on the row, return true if it qualifies. |
558 | * <p> |
559 | * A two dimensional array is to be used to pass around a AND's and OR's in |
560 | * conjunctive normal form. The top slot of the 2 dimensional array is |
561 | * optimized for the more frequent where no OR's are present. The first |
562 | * array slot is always a list of AND's to be treated as described above |
563 | * for single dimensional AND qualifier arrays. The subsequent slots are |
564 | * to be treated as AND'd arrays or OR's. Thus the 2 dimensional array |
565 | * qual[][] argument is to be treated as the following, note if |
566 | * qual.length = 1 then only the first array is valid and it is and an |
567 | * array of and clauses: |
568 | * |
569 | * (qual[0][0] and qual[0][0] ... and qual[0][qual[0].length - 1]) |
570 | * and |
571 | * (qual[1][0] or qual[1][1] ... or qual[1][qual[1].length - 1]) |
572 | * and |
573 | * (qual[2][0] or qual[2][1] ... or qual[2][qual[2].length - 1]) |
574 | * ... |
575 | * and |
576 | * (qual[qual.length - 1][0] or qual[1][1] ... or qual[1][2]) |
577 | * |
578 | * |
579 | * @return true if the row qualifies. |
580 | * |
581 | * @param row The row being qualified. |
582 | * @param qual_list 2 dimensional array representing conjunctive |
583 | * normal form of simple qualifiers. |
584 | * |
585 | * @exception StandardException Standard exception policy. |
586 | **/ |
587 | public static final boolean qualifyRow( |
588 | Object[] row, |
589 | Qualifier[][] qual_list) |
590 | throws StandardException |
591 | { |
592 | boolean row_qualifies = true; |
593 | |
594 | if (SanityManager.DEBUG) |
595 | { |
596 | SanityManager.ASSERT(row != null); |
597 | } |
598 | |
599 | // First do the qual[0] which is an array of qualifer terms. |
600 | |
601 | if (SanityManager.DEBUG) |
602 | { |
603 | // routine should not be called if there is no qualifier |
604 | SanityManager.ASSERT(qual_list != null); |
605 | SanityManager.ASSERT(qual_list.length > 0); |
606 | } |
607 | |
608 | for (int i = 0; i < qual_list[0].length; i++) |
609 | { |
610 | // process each AND clause |
611 | |
612 | row_qualifies = false; |
613 | |
614 | // process each OR clause. |
615 | |
616 | Qualifier q = qual_list[0][i]; |
617 | |
618 | // Get the column from the possibly partial row, of the |
619 | // q.getColumnId()'th column in the full row. |
620 | DataValueDescriptor columnValue = |
621 | (DataValueDescriptor) row[q.getColumnId()]; |
622 | |
623 | row_qualifies = |
624 | columnValue.compare( |
625 | q.getOperator(), |
626 | q.getOrderable(), |
627 | q.getOrderedNulls(), |
628 | q.getUnknownRV()); |
629 | |
630 | if (q.negateCompareResult()) |
631 | row_qualifies = !row_qualifies; |
632 | |
633 | // Once an AND fails the whole Qualification fails - do a return! |
634 | if (!row_qualifies) |
635 | return(false); |
636 | } |
637 | |
638 | // all the qual[0] and terms passed, now process the OR clauses |
639 | |
640 | for (int and_idx = 1; and_idx < qual_list.length; and_idx++) |
641 | { |
642 | // loop through each of the "and" clause. |
643 | |
644 | row_qualifies = false; |
645 | |
646 | if (SanityManager.DEBUG) |
647 | { |
648 | // Each OR clause must be non-empty. |
649 | SanityManager.ASSERT(qual_list[and_idx].length > 0); |
650 | } |
651 | |
652 | for (int or_idx = 0; or_idx < qual_list[and_idx].length; or_idx++) |
653 | { |
654 | // Apply one qualifier to the row. |
655 | Qualifier q = qual_list[and_idx][or_idx]; |
656 | int col_id = q.getColumnId(); |
657 | |
658 | if (SanityManager.DEBUG) |
659 | { |
660 | SanityManager.ASSERT( |
661 | (col_id < row.length), |
662 | "Qualifier is referencing a column not in the row."); |
663 | } |
664 | |
665 | // Get the column from the possibly partial row, of the |
666 | // q.getColumnId()'th column in the full row. |
667 | DataValueDescriptor columnValue = |
668 | (DataValueDescriptor) row[q.getColumnId()]; |
669 | |
670 | if (SanityManager.DEBUG) |
671 | { |
672 | if (columnValue == null) |
673 | SanityManager.THROWASSERT( |
674 | "1:row = " + RowUtil.toString(row) + |
675 | "row.length = " + row.length + |
676 | ";q.getColumnId() = " + q.getColumnId()); |
677 | } |
678 | |
679 | // do the compare between the column value and value in the |
680 | // qualifier. |
681 | row_qualifies = |
682 | columnValue.compare( |
683 | q.getOperator(), |
684 | q.getOrderable(), |
685 | q.getOrderedNulls(), |
686 | q.getUnknownRV()); |
687 | |
688 | if (q.negateCompareResult()) |
689 | row_qualifies = !row_qualifies; |
690 | |
691 | // SanityManager.DEBUG_PRINT("StoredPage.qual", "processing qual[" + and_idx + "][" + or_idx + "] = " + qual_list[and_idx][or_idx] ); |
692 | |
693 | // SanityManager.DEBUG_PRINT("StoredPage.qual", "value = " + row_qualifies); |
694 | |
695 | // processing "OR" clauses, so as soon as one is true, break |
696 | // to go and process next AND clause. |
697 | if (row_qualifies) |
698 | break; |
699 | |
700 | } |
701 | |
702 | // The qualifier list represented a set of "AND'd" |
703 | // qualifications so as soon as one is false processing is done. |
704 | if (!row_qualifies) |
705 | break; |
706 | } |
707 | |
708 | return(row_qualifies); |
709 | } |
710 | |
711 | } |