EMMA Coverage Report (generated Wed Jun 28 22:15:27 PDT 2006)
[all classes][org.apache.derby.iapi.store.access]

COVERAGE SUMMARY FOR SOURCE FILE [BackingStoreHashtable.java]

nameclass, %method, %block, %line, %
BackingStoreHashtable.java100% (2/2)95%  (19/20)84%  (584/693)89%  (141.6/159)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class BackingStoreHashtable100% (1/1)94%  (16/17)84%  (530/630)90%  (125.6/139)
<static initializer> 100% (1/1)91%  (10/11)90%  (0.9/1)
BackingStoreHashtable (): void 0%   (0/1)0%   (0/3)0%   (0/1)
BackingStoreHashtable (TransactionController, RowSource, int [], boolean, lon... 100% (1/1)71%  (96/135)85%  (23/27)
add_row_to_hash_table (Hashtable, Object, Object []): void 100% (1/1)100% (58/58)100% (16/16)
cloneRow (Object []): Object [] 100% (1/1)100% (26/26)100% (5/5)
close (): void 100% (1/1)100% (13/13)100% (5/5)
doSpaceAccounting (Object [], boolean): void 100% (1/1)100% (29/29)100% (6/6)
elements (): Enumeration 100% (1/1)100% (12/12)100% (3/3)
get (Object): Object 100% (1/1)100% (17/17)100% (4/4)
getAllRuntimeStats (Properties): void 100% (1/1)100% (8/8)100% (3/3)
getEstimatedMemUsage (Object []): long 100% (1/1)100% (36/36)100% (7/7)
getNextRowFromRowSource (): Object [] 100% (1/1)76%  (52/68)93%  (13/14)
put (boolean, Object []): boolean 100% (1/1)75%  (57/76)87%  (13/15)
remove (Object): Object 100% (1/1)53%  (9/17)68%  (2.7/4)
setAuxillaryRuntimeStats (Properties): void 100% (1/1)100% (4/4)100% (2/2)
size (): int 100% (1/1)47%  (7/15)67%  (2/3)
spillToDisk (Hashtable, Object, Object []): boolean 100% (1/1)94%  (96/102)87%  (20/23)
     
class BackingStoreHashtable$BackingStoreHashtableEnumeration100% (1/1)100% (3/3)86%  (54/63)80%  (16/20)
BackingStoreHashtable$BackingStoreHashtableEnumeration (BackingStoreHashtable... 100% (1/1)83%  (20/24)75%  (6/8)
hasMoreElements (): boolean 100% (1/1)90%  (19/21)86%  (6/7)
nextElement (): Object 100% (1/1)83%  (15/18)80%  (4/5)

1/*
2 
3   Derby - Class org.apache.derby.iapi.store.access.BackingStoreHashtable
4 
5   Copyright 1999, 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 
21package org.apache.derby.iapi.store.access;
22 
23import org.apache.derby.iapi.services.sanity.SanityManager;
24 
25import org.apache.derby.iapi.services.io.Storable;
26 
27import org.apache.derby.iapi.error.StandardException; 
28 
29import org.apache.derby.iapi.types.CloneableObject;
30import org.apache.derby.iapi.types.DataValueDescriptor;
31 
32import org.apache.derby.iapi.services.cache.ClassSize;
33 
34import java.util.Enumeration;
35import java.util.Hashtable;
36import java.util.Properties; 
37import java.util.Vector;
38import java.util.NoSuchElementException;
39 
40/**
41A BackingStoreHashtable is a utility class which will store a set of rows into
42an in memory hash table, or overflow the hash table to a tempory on disk 
43structure.
44<p>
45All rows must contain the same number of columns, and the column at position
46N of all the rows must have the same format id.  If the BackingStoreHashtable needs to be
47overflowed to disk, then an arbitrary row will be chosen and used as a template
48for creating the underlying overflow container.
49 
50<p>
51The hash table will be built logically as follows (actual implementation
52may differ).  The important points are that the hash value is the standard
53java hash value on the row[key_column_numbers[0], if key_column_numbers.length is 1,
54or row[key_column_numbers[0, 1, ...]] if key_column_numbers.length > 1, 
55and that duplicate detection is done by the standard java duplicate detection provided by 
56java.util.Hashtable.
57<p>
58import java.util.Hashtable;
59 
60hash_table = new Hashtable();
61 
62Object[] row;
63boolean  needsToClone = rowSource.needsToClone();
64 
65while((row = rowSource.getNextRowFromRowSource()) != null)
66{
67    if (needsToClone)
68        row = clone_row_from_row(row);
69 
70        Object key = KeyHasher.buildHashKey(row, key_column_numbers);
71 
72    if ((duplicate_value = 
73        hash_table.put(key, row)) != null)
74    {
75        Vector row_vec;
76 
77        // inserted a duplicate
78        if ((duplicate_value instanceof vector))
79        {
80            row_vec = (Vector) duplicate_value;
81        }
82        else
83        {
84            // allocate vector to hold duplicates
85            row_vec = new Vector(2);
86 
87            // insert original row into vector
88            row_vec.addElement(duplicate_value);
89 
90            // put the vector as the data rather than the row
91            hash_table.put(key, row_vec);
92        }
93        
94        // insert new row into vector
95        row_vec.addElement(row);
96    }
97}
98 
99**/
100 
101public class BackingStoreHashtable
102{
103 
104    /**************************************************************************
105     * Fields of the class
106     **************************************************************************
107     */
108    private TransactionController tc;
109    private Hashtable   hash_table;
110    private int[]       key_column_numbers;
111    private boolean     remove_duplicates;
112        private boolean                skipNullKeyColumns;
113    private Properties  auxillary_runtimestats;
114        private RowSource        row_source;
115    /* If max_inmemory_rowcnt > 0 then use that to decide when to spill to disk.
116     * Otherwise compute max_inmemory_size based on the JVM memory size when the BackingStoreHashtable
117     * is constructed and use that to decide when to spill to disk.
118     */
119    private long max_inmemory_rowcnt;
120    private long inmemory_rowcnt;
121    private long max_inmemory_size;
122    private boolean keepAfterCommit;
123 
124    /**
125     * The estimated number of bytes used by Vector(0)
126     */  
127    private final static int vectorSize = ClassSize.estimateBaseFromCatalog(java.util.Vector.class);
128    
129    private DiskHashtable diskHashtable;
130 
131    /**************************************************************************
132     * Constructors for This class:
133     **************************************************************************
134     */
135    private BackingStoreHashtable(){}
136 
137    /**
138     * Create the BackingStoreHashtable from a row source.
139     * <p>
140     * This routine drains the RowSource.  The performance characteristics
141     * depends on the number of rows inserted and the parameters to the 
142     * constructor.  
143     * <p>
144     * If the number of rows is <= "max_inmemory_rowcnt", then the rows are
145     * inserted into a java.util.Hashtable.  In this case no 
146     * TransactionController is necessary, a "null" tc is valid.
147     * <p>
148     * If the number of rows is > "max_inmemory_rowcnt", then the rows will
149     * be all placed in some sort of Access temporary file on disk.  This 
150     * case requires a valid TransactionController.
151     *
152     * @param tc                An open TransactionController to be used if the
153     *                          hash table needs to overflow to disk.
154     *
155     * @param row_source        RowSource to read rows from.
156     *
157     * @param key_column_numbers The column numbers of the columns in the
158     *                          scan result row to be the key to the Hashtable.
159     *                          "0" is the first column in the scan result
160     *                          row (which may be different than the first
161     *                          row in the table of the scan).
162     *
163     * @param remove_duplicates Should the Hashtable automatically remove
164     *                          duplicates, or should it create the Vector of
165     *                          duplicates?
166     *
167     * @param estimated_rowcnt  The estimated number of rows in the hash table.
168     *                          Pass in -1 if there is no estimate.
169     *
170     * @param max_inmemory_rowcnt
171     *                          The maximum number of rows to insert into the 
172     *                          inmemory Hash table before overflowing to disk.
173     *                          Pass in -1 if there is no maximum.
174     *
175     * @param initialCapacity   If not "-1" used to initialize the java 
176     *                          Hashtable.
177     *
178     * @param loadFactor        If not "-1" used to initialize the java 
179     *                          Hashtable.
180         *
181         * @param skipNullKeyColumns        Skip rows with a null key column, if true.
182     *
183     * @param keepAfterCommit If true the hash table is kept after a commit,
184     *                        if false the hash table is dropped on the next commit.
185     *
186     *
187         * @exception  StandardException  Standard exception policy.
188     **/
189    public BackingStoreHashtable(
190    TransactionController   tc,
191    RowSource               row_source,
192    int[]                   key_column_numbers,
193    boolean                 remove_duplicates,
194    long                    estimated_rowcnt,
195    long                    max_inmemory_rowcnt,
196    int                     initialCapacity,
197    float                   loadFactor,
198        boolean                                        skipNullKeyColumns,
199    boolean                 keepAfterCommit)
200        throws StandardException
201    {
202        this.key_column_numbers    = key_column_numbers;
203        this.remove_duplicates    = remove_duplicates;
204                this.row_source                           = row_source;
205                this.skipNullKeyColumns           = skipNullKeyColumns;
206        this.max_inmemory_rowcnt = max_inmemory_rowcnt;
207        if( max_inmemory_rowcnt > 0)
208            max_inmemory_size = Long.MAX_VALUE;
209        else
210            max_inmemory_size = Runtime.getRuntime().totalMemory()/100;
211        this.tc = tc;
212        this.keepAfterCommit = keepAfterCommit;
213 
214        Object[] row;
215 
216        // use passed in capacity and loadfactor if not -1, you must specify
217        // capacity if you want to specify loadfactor.
218        if (initialCapacity != -1)
219        {
220            hash_table = 
221                ((loadFactor == -1) ? 
222                     new Hashtable(initialCapacity) : 
223                     new Hashtable(initialCapacity, loadFactor));
224        }
225        else
226        {
227            /* We want to create the hash table based on the estimated row
228             * count if a) we have an estimated row count (i.e. it's greater
229             * than zero) and b) we think we can create a hash table to
230             * hold the estimated row count without running out of memory.
231             * The check for "b" is required because, for deeply nested
232             * queries and/or queries with a high number of tables in
233             * their FROM lists, the optimizer can end up calculating
234             * some very high row count estimates--even up to the point of
235             * Double.POSITIVE_INFINITY (see DERBY-1259 for an explanation
236             * of how that can happen).  In that case any attempts to
237             * create a Hashtable of size estimated_rowcnt can cause
238             * OutOfMemory errors when we try to create the Hashtable.
239             * So as a "red flag" for that kind of situation, we check to
240             * see if the estimated row count is greater than the max
241             * in-memory size for this table.  Unit-wise this comparison
242             * is relatively meaningless: rows vs bytes.  But if our
243             * estimated row count is greater than the max number of
244             * in-memory bytes that we're allowed to consume, then
245             * it's very likely that creating a Hashtable with a capacity
246             * of estimated_rowcnt will lead to memory problems.  So in
247             * that particular case we leave hash_table null here and
248             * initialize it further below, using the estimated in-memory
249             * size of the first row to figure out what a reasonable size
250             * for the Hashtable might be.
251             */
252            hash_table = 
253                (((estimated_rowcnt <= 0) || (row_source == null)) ?
254                     new Hashtable() :
255                     (estimated_rowcnt < max_inmemory_size) ?
256                         new Hashtable((int) estimated_rowcnt) :
257                         null);
258        }
259 
260        if (row_source != null)
261        {
262            boolean needsToClone = row_source.needsToClone();
263 
264            while ((row = getNextRowFromRowSource()) != null)
265            {
266                // If we haven't initialized the hash_table yet then that's
267                // because a Hashtable with capacity estimated_rowcnt would
268                // probably cause memory problems.  So look at the first row
269                // that we found and use that to create the hash table with
270                // an initial capacity such that, if it was completely full,
271                // it would still satisfy the max_inmemory condition.  Note
272                // that this isn't a hard limit--the hash table can grow if
273                // needed.
274                if (hash_table == null)
275                {
276                    // Check to see how much memory we think the first row
277                    // is going to take, and then use that to set the initial
278                    // capacity of the Hashtable.
279                    double rowUsage = getEstimatedMemUsage(row);
280                    hash_table = new Hashtable((int)(max_inmemory_size / rowUsage));
281                }
282 
283                if (needsToClone)
284                {
285                    row = cloneRow(row);
286                }
287 
288                Object key = 
289                    KeyHasher.buildHashKey(row, key_column_numbers);
290 
291                add_row_to_hash_table(hash_table, key, row);
292            }
293        }
294 
295        // In the (unlikely) event that we received a "red flag" estimated_rowcnt
296        // that is too big (see comments above), it's possible that, if row_source
297        // was null or else didn't have any rows, hash_table could still be null
298        // at this point.  So we initialize it to an empty hashtable (representing
299        // an empty result set) so that calls to other methods on this
300        // BackingStoreHashtable (ex. "size()") will have a working hash_table
301        // on which to operate.
302        if (hash_table == null)
303            hash_table = new Hashtable();
304    }
305 
306    /**************************************************************************
307     * Private/Protected methods of This class:
308     **************************************************************************
309     */
310 
311        /**
312         * Call method to either get next row or next row with non-null
313         * key columns.
314         *
315     *
316         * @exception  StandardException  Standard exception policy.
317         */
318        private Object[] getNextRowFromRowSource()
319                throws StandardException
320        {
321                Object[] row = row_source.getNextRowFromRowSource();
322 
323                if (skipNullKeyColumns)
324                {
325                        while (row != null)
326                        {
327                                // Are any key columns null?
328                                int index = 0;
329                                for ( ; index < key_column_numbers.length; index++)
330                                {
331                                        if (SanityManager.DEBUG)
332                                        {
333                                                if (! (row[key_column_numbers[index]] instanceof Storable))
334                                                {
335                                                        SanityManager.THROWASSERT(
336                                                                "row[key_column_numbers[index]] expected to be Storable, not " +
337                                                                row[key_column_numbers[index]].getClass().getName());
338                                                }
339                                        }
340                                        Storable storable = (Storable) row[key_column_numbers[index]];
341                                        if (storable.isNull())
342                                        {
343                                                break;
344                                        }
345                                }
346                                // No null key columns
347                                if (index == key_column_numbers.length)
348                                {
349                                        return row;
350                                }
351                                // 1 or more null key columns
352                                row = row_source.getNextRowFromRowSource();
353                        }
354                }
355                return row;
356        }
357 
358    /**
359     * Return a cloned copy of the row.
360     *
361         * @return The cloned row row to use.
362     *
363         * @exception  StandardException  Standard exception policy.
364     **/
365    static Object[] cloneRow(Object[] old_row)
366        throws StandardException
367    {
368        Object[] new_row = new DataValueDescriptor[old_row.length];
369 
370                // the only difference between getClone and cloneObject is cloneObject does
371                // not objectify a stream.  We use getClone here.  Beetle 4896.
372        for (int i = 0; i < old_row.length; i++)
373        {
374            if( old_row[i] != null)
375                new_row[i] = ((DataValueDescriptor) old_row[i]).getClone();
376        }
377 
378        return(new_row);
379    }
380 
381    /**
382     * Do the work to add one row to the hash table.
383     * <p>
384     *
385     * @param row               Row to add to the hash table.
386     * @param hash_table        The java HashTable to load into.
387     *
388         * @exception  StandardException  Standard exception policy.
389     **/
390    private void add_row_to_hash_table(
391    Hashtable   hash_table,
392    Object      key,
393    Object[]    row)
394                throws StandardException
395    {
396        if( spillToDisk( hash_table, key, row))
397            return;
398        
399        Object  duplicate_value = null;
400 
401        if ((duplicate_value = hash_table.put(key, row)) == null)
402            doSpaceAccounting( row, false);
403        else
404        {
405            if (!remove_duplicates)
406            {
407                Vector row_vec;
408 
409                // inserted a duplicate
410                if ((duplicate_value instanceof Vector))
411                {
412                    doSpaceAccounting( row, false);
413                    row_vec = (Vector) duplicate_value;
414                }
415                else
416                {
417                    // allocate vector to hold duplicates
418                    row_vec = new Vector(2);
419 
420                    // insert original row into vector
421                    row_vec.addElement(duplicate_value);
422                    doSpaceAccounting( row, true);
423                }
424 
425                // insert new row into vector
426                row_vec.addElement(row);
427 
428                // store vector of rows back into hash table,
429                // overwriting the duplicate key that was 
430                // inserted.
431                hash_table.put(key, row_vec);
432            }
433        }
434 
435        row = null;
436    }
437 
438    private void doSpaceAccounting( Object[] row,
439                                    boolean firstDuplicate)
440    {
441        inmemory_rowcnt++;
442        if( max_inmemory_rowcnt <= 0)
443        {
444            max_inmemory_size -= getEstimatedMemUsage(row);
445            if( firstDuplicate)
446                max_inmemory_size -= vectorSize;
447        }
448    } // end of doSpaceAccounting
449 
450    /**
451     * Determine whether a new row should be spilled to disk and, if so, do it.
452     *
453     * @param hash_table The in-memory hash table
454     * @param key The row's key
455     * @param row
456     *
457     * @return true if the row was spilled to disk, false if not
458     *
459     * @exception  StandardException  Standard exception policy.
460     */
461    private boolean spillToDisk( Hashtable   hash_table,
462                                 Object      key,
463                                 Object[]    row)
464                throws StandardException
465    {
466        // Once we have started spilling all new rows will go to disk, even if we have freed up some
467        // memory by moving duplicates to disk. This simplifies handling of duplicates and accounting.
468        if( diskHashtable == null)
469        {
470            if( max_inmemory_rowcnt > 0)
471            {
472                if( inmemory_rowcnt < max_inmemory_rowcnt)
473                    return false; // Do not spill
474            }
475            else if( max_inmemory_size > 0)
476                return false;
477            // Want to start spilling
478            if( ! (row instanceof DataValueDescriptor[]))
479            {
480                if( SanityManager.DEBUG)
481                    SanityManager.THROWASSERT( "BackingStoreHashtable row is not DataValueDescriptor[]");
482                // Do not know how to put it on disk
483                return false;
484            }
485            diskHashtable = new DiskHashtable( tc,
486                                               (DataValueDescriptor[]) row,
487                                               key_column_numbers,
488                                               remove_duplicates,
489                                               keepAfterCommit);
490        }
491        
492        Object duplicateValue = hash_table.get( key);
493        if( duplicateValue != null)
494        {
495            if( remove_duplicates)
496                return true; // a degenerate case of spilling
497            // If we are keeping duplicates then move all the duplicates from memory to disk
498            // This simplifies finding duplicates: they are either all in memory or all on disk.
499            if( duplicateValue instanceof Vector)
500            {
501                Vector duplicateVec = (Vector) duplicateValue;
502                for( int i = duplicateVec.size() - 1; i >= 0; i--)
503                {
504                    Object[] dupRow = (Object[]) duplicateVec.elementAt(i);
505                    diskHashtable.put( key, dupRow);
506                }
507            }
508            else
509                diskHashtable.put( key, (Object []) duplicateValue);
510            hash_table.remove( key);
511        }
512        diskHashtable.put( key, row);
513        return true;
514    } // end of spillToDisk
515 
516    /**
517     * Take a row and return an estimate as to how much memory that
518     * row will consume.
519     * 
520     * @param row The row for which we want to know the memory usage.
521     * @return A guess as to how much memory the current row will
522     *  use.
523     */
524    private long getEstimatedMemUsage(Object [] row)
525    {
526        long rowMem = 0;
527        for( int i = 0; i < row.length; i++)
528        {
529            if (row[i] instanceof DataValueDescriptor)
530                rowMem += ((DataValueDescriptor) row[i]).estimateMemoryUsage();
531            rowMem += ClassSize.refSize;
532        }
533 
534        rowMem += ClassSize.refSize;
535        return rowMem;
536    }
537 
538    /**************************************************************************
539     * Public Methods of This class:
540     **************************************************************************
541     */
542 
543    /**
544     * Close the BackingStoreHashtable.
545     * <p>
546     * Perform any necessary cleanup after finishing with the hashtable.  Will
547     * deallocate/dereference objects as necessary.  If the table has gone
548     * to disk this will drop any on disk files used to support the hash table.
549     * <p>
550     *
551         * @exception  StandardException  Standard exception policy.
552     **/
553    public void close() 
554                throws StandardException
555    {
556        hash_table = null;
557        if( diskHashtable != null)
558        {
559            diskHashtable.close();
560            diskHashtable = null;
561        }
562        return;
563    }
564 
565    /**
566     * Return an Enumeration that can be used to scan entire table.
567     * <p>
568     * RESOLVE - is it worth it to support this routine when we have a
569     *           disk overflow hash table?
570     *
571         * @return The Enumeration.
572     *
573         * @exception  StandardException  Standard exception policy.
574     **/
575    public Enumeration elements()
576        throws StandardException
577    {
578        if( diskHashtable == null)
579            return(hash_table.elements());
580        return new BackingStoreHashtableEnumeration();
581    }
582 
583    /**
584     * get data associated with given key.
585     * <p>
586     * There are 2 different types of objects returned from this routine.
587     * <p>
588         * In both cases, the key value is either the object stored in 
589     * row[key_column_numbers[0]], if key_column_numbers.length is 1, 
590     * otherwise it is a KeyHasher containing
591         * the objects stored in row[key_column_numbers[0, 1, ...]].
592     * For every qualifying unique row value an entry is placed into the 
593     * Hashtable.
594     * <p>
595     * For row values with duplicates, the value of the data is a Vector of
596     * rows.
597     * <p>
598     * The caller will have to call "instanceof" on the data value
599     * object if duplicates are expected, to determine if the data value
600     * of the Hashtable entry is a row or is a Vector of rows.
601     * <p>
602     * The BackingStoreHashtable "owns" the objects returned from the get()
603     * routine.  They remain valid until the next access to the 
604     * BackingStoreHashtable.  If the client needs to keep references to these
605     * objects, it should clone copies of the objects.  A valid 
606     * BackingStoreHashtable can place all rows into a disk based conglomerate,
607     * declare a row buffer and then reuse that row buffer for every get()
608     * call.
609     *
610         * @return The value to which the key is mapped in this hashtable; 
611     *         null if the key is not mapped to any value in this hashtable.
612     *
613     * @param key    The key to hash on.
614     *
615         * @exception  StandardException  Standard exception policy.
616     **/
617    public Object get(Object key)
618                throws StandardException
619    {
620        Object obj = hash_table.get(key);
621        if( diskHashtable == null || obj != null)
622            return obj;
623        return diskHashtable.get( key);
624    }
625 
626    /**
627     * Return runtime stats to caller by adding them to prop.
628     * <p>
629     *
630     * @param prop   The set of properties to append to.
631     *
632         * @exception  StandardException  Standard exception policy.
633     **/
634    public void getAllRuntimeStats(Properties   prop)
635                throws StandardException
636    {
637        if (auxillary_runtimestats != null)
638            org.apache.derby.iapi.util.PropertyUtil.copyProperties(auxillary_runtimestats, prop);
639    }
640 
641    /**
642     * remove a row from the hash table.
643     * <p>
644     * a remove of a duplicate removes the entire duplicate list.
645     *
646     * @param key          The key of the row to remove.
647     *
648         * @exception  StandardException  Standard exception policy.
649     **/
650    public Object remove(
651    Object      key)
652                throws StandardException
653    {
654        Object obj = hash_table.remove(key);
655        if( obj != null || diskHashtable == null)
656            return obj;
657        return diskHashtable.remove(key);
658    }
659 
660    /**
661     * Set the auxillary runtime stats.
662     * <p>
663     * getRuntimeStats() will return both the auxillary stats and any
664     * BackingStoreHashtable() specific stats.  Note that each call to
665     * setAuxillaryRuntimeStats() overwrites the Property set that was
666     * set previously.
667     *
668     * @param prop   The set of properties to append from.
669     *
670         * @exception  StandardException  Standard exception policy.
671     **/
672    public void setAuxillaryRuntimeStats(Properties   prop)
673                throws StandardException
674    {
675        auxillary_runtimestats = prop;
676    }
677 
678    /**
679     * Put a row into the hash table.
680     * <p>
681     * The in memory hash table will need to keep a reference to the row
682     * after the put call has returned.  If "needsToClone" is true then the
683     * hash table will make a copy of the row and put that, else if 
684     * "needsToClone" is false then the hash table will keep a reference to
685     * the row passed in and no copy will be made.
686     * <p>
687     * If rouine returns false, then no reference is kept to the duplicate
688     * row which was rejected (thus allowing caller to reuse the object).
689     *
690     * @param needsToClone does this routine have to make a copy of the row,
691     *                     in order to keep a reference to it after return?
692     * @param row          The row to insert into the table.
693     *
694         * @return true if row was inserted into the hash table.  Returns
695     *              false if the BackingStoreHashtable is eliminating 
696     *              duplicates, and the row being inserted is a duplicate,
697         *                                or if we are skipping rows with 1 or more null key columns
698         *                                and we find a null key column.
699     *
700         * @exception  StandardException  Standard exception policy.
701     **/
702    public boolean put(
703    boolean     needsToClone,
704    Object[]    row)
705                throws StandardException
706    {
707                // Are any key columns null?
708                if (skipNullKeyColumns)
709                {
710                        int index = 0;
711                        for ( ; index < key_column_numbers.length; index++)
712                        {
713                                if (SanityManager.DEBUG)
714                                {
715                                        if (! (row[key_column_numbers[index]] instanceof Storable))
716                                        {
717                                                SanityManager.THROWASSERT(
718                                                        "row[key_column_numbers[index]] expected to be Storable, not " +
719                                                        row[key_column_numbers[index]].getClass().getName());
720                                        }
721                                }
722                                Storable storable = (Storable) row[key_column_numbers[index]];
723                                if (storable.isNull())
724                                {
725                                        return false;
726                                }
727                        }
728                }
729 
730        if (needsToClone)
731        {
732            row = cloneRow(row);
733        }
734 
735        Object key = KeyHasher.buildHashKey(row, key_column_numbers);
736 
737        if ((remove_duplicates) && (get(key) != null))
738        {
739            return(false);
740        }
741        else
742        {
743            add_row_to_hash_table(hash_table, key, row);
744            return(true);
745        }
746    }
747 
748    /**
749     * Return number of unique rows in the hash table.
750     * <p>
751     *
752         * @return The number of unique rows in the hash table.
753     *
754         * @exception  StandardException  Standard exception policy.
755     **/
756    public int size()
757                throws StandardException
758    {
759        if( diskHashtable == null)
760            return(hash_table.size());
761        return hash_table.size() + diskHashtable.size();
762    }
763 
764    private class BackingStoreHashtableEnumeration implements Enumeration
765    {
766        private Enumeration memoryEnumeration;
767        private Enumeration diskEnumeration;
768 
769        BackingStoreHashtableEnumeration()
770        {
771            memoryEnumeration = hash_table.elements();
772            if( diskHashtable != null)
773            {
774                try
775                {
776                    diskEnumeration = diskHashtable.elements();
777                }
778                catch( StandardException se)
779                {
780                    diskEnumeration = null;
781                }
782            }
783        }
784        
785        public boolean hasMoreElements()
786        {
787            if( memoryEnumeration != null)
788            {
789                if( memoryEnumeration.hasMoreElements())
790                    return true;
791                memoryEnumeration = null;
792            }
793            if( diskEnumeration == null)
794                return false;
795            return diskEnumeration.hasMoreElements();
796        }
797 
798        public Object nextElement() throws NoSuchElementException
799        {
800            if( memoryEnumeration != null)
801            {
802                if( memoryEnumeration.hasMoreElements())
803                    return memoryEnumeration.nextElement();
804                memoryEnumeration = null;
805            }
806            return diskEnumeration.nextElement();
807        }
808    } // end of class BackingStoreHashtableEnumeration
809}

[all classes][org.apache.derby.iapi.store.access]
EMMA 2.0.5312 (C) Vladimir Roubtsov