KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > execute > TemporaryRowHolderImpl


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.execute.TemporaryRowHolderImpl
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.sql.execute;
23
24 import org.apache.derby.iapi.services.sanity.SanityManager;
25 import org.apache.derby.iapi.error.StandardException;
26 import org.apache.derby.iapi.sql.execute.CursorResultSet;
27 import org.apache.derby.iapi.sql.execute.ExecRow;
28 import org.apache.derby.iapi.sql.execute.ExecutionFactory;
29 import org.apache.derby.iapi.sql.execute.TemporaryRowHolder;
30 import org.apache.derby.iapi.sql.Activation;
31 import org.apache.derby.iapi.sql.ResultDescription;
32 import org.apache.derby.iapi.store.access.ConglomerateController;
33 import org.apache.derby.iapi.store.access.ScanController;
34 import org.apache.derby.iapi.store.access.TransactionController;
35
36 import org.apache.derby.iapi.types.CloneableObject;
37 import org.apache.derby.iapi.types.RowLocation;
38 import org.apache.derby.iapi.types.DataValueDescriptor;
39 import org.apache.derby.iapi.types.SQLRef;
40 import org.apache.derby.iapi.types.SQLLongint;
41
42
43 import org.apache.derby.iapi.services.io.FormatableBitSet;
44 import java.util.Properties JavaDoc;
45
46 /**
47  * This is a class that is used to temporarily
48  * (non-persistently) hold rows that are used in
49  * language execution. It will store them in an
50  * array, or a temporary conglomerate, depending
51  * on the number of rows.
52  * <p>
53  * It is used for deferred DML processing.
54  *
55  * @author jamie
56  */

57 class TemporaryRowHolderImpl implements TemporaryRowHolder
58 {
59     public static final int DEFAULT_OVERFLOWTHRESHOLD = 5;
60
61     protected static final int STATE_UNINIT = 0;
62     protected static final int STATE_INSERT = 1;
63     protected static final int STATE_DRAIN = 2;
64
65
66     protected ExecRow[] rowArray;
67     protected int lastArraySlot;
68     private int numRowsIn;
69     protected int state = STATE_UNINIT;
70
71     protected long CID;
72     private boolean conglomCreated;
73     private ConglomerateController cc;
74     private Properties JavaDoc properties;
75     private ScanController scan;
76     private ResultDescription resultDescription;
77     /** Activation object with local state information. */
78     Activation activation;
79
80     private boolean isUniqueStream;
81
82     /* beetle 3865 updateable cursor use index. A virtual memory heap is a heap that has in-memory
83      * part to get better performance, less overhead. No position index needed. We read from and write
84      * to the in-memory part as much as possible. And we can insert after we start retrieving results.
85      * Could be used for other things too.
86      */

87     private boolean isVirtualMemHeap;
88     private boolean uniqueIndexCreated;
89     private boolean positionIndexCreated;
90     private long uniqueIndexConglomId;
91     private long positionIndexConglomId;
92     private ConglomerateController uniqueIndex_cc;
93     private ConglomerateController positionIndex_cc;
94     private DataValueDescriptor[] uniqueIndexRow = null;
95     private DataValueDescriptor[] positionIndexRow = null;
96     private RowLocation destRowLocation; //row location in the temporary conglomerate
97
private SQLLongint position_sqllong;
98     
99
100     /**
101      * Uses the default overflow to
102      * a conglomerate threshold (5).
103      *
104      * @param activation the activation
105      * @param properties the properties of the original table. Used
106      * to help the store use optimal page size, etc.
107      * @param resultDescription the result description. Relevant for the getResultDescription
108      * call on the result set returned by getResultSet. May be null
109      */

110     public TemporaryRowHolderImpl
111     (
112         Activation activation,
113         Properties JavaDoc properties,
114         ResultDescription resultDescription
115     )
116     {
117         this(activation, properties, resultDescription,
118              DEFAULT_OVERFLOWTHRESHOLD, false, false);
119     }
120     
121     /**
122      * Uses the default overflow to
123      * a conglomerate threshold (5).
124      *
125      * @param activation the activation
126      * @param properties the properties of the original table. Used
127      * to help the store use optimal page size, etc.
128      * @param resultDescription the result description. Relevant for the getResultDescription
129      * call on the result set returned by getResultSet. May be null
130      * @param isUniqueStream - true , if it has to be temporary row holder unique stream
131      */

132     public TemporaryRowHolderImpl
133     (
134         Activation activation,
135         Properties JavaDoc properties,
136         ResultDescription resultDescription,
137         boolean isUniqueStream
138     )
139     {
140         this(activation, properties, resultDescription, 1, isUniqueStream,
141              false);
142     }
143
144
145     /**
146      * Create a temporary row holder with the defined overflow to conglom
147      *
148      * @param activation the activation
149      * @param properties the properties of the original table. Used
150      * to help the store use optimal page size, etc.
151      * @param resultDescription the result description. Relevant for the getResultDescription
152      * call on the result set returned by getResultSet. May be null
153      * @param overflowToConglomThreshold on an attempt to insert
154      * this number of rows, the rows will be put
155      * into a temporary conglomerate.
156      */

157     public TemporaryRowHolderImpl
158     (
159         Activation activation,
160         Properties JavaDoc properties,
161         ResultDescription resultDescription,
162         int overflowToConglomThreshold,
163         boolean isUniqueStream,
164         boolean isVirtualMemHeap
165     )
166     {
167         if (SanityManager.DEBUG)
168         {
169             if (overflowToConglomThreshold <= 0)
170             {
171                 SanityManager.THROWASSERT("It is assumed that "+
172                     "the overflow threshold is > 0. "+
173                     "If you you need to change this you have to recode some of "+
174                     "this class.");
175             }
176         }
177
178         this.activation = activation;
179         this.properties = properties;
180         this.resultDescription = resultDescription;
181         this.isUniqueStream = isUniqueStream;
182         this.isVirtualMemHeap = isVirtualMemHeap;
183         rowArray = new ExecRow[overflowToConglomThreshold];
184         lastArraySlot = -1;
185     }
186
187     /* Avoid materializing a stream just because it goes through a temp table. It is OK to
188      * have a stream in the temp table (in memory or spilled to disk). The assumption is
189      * that one stream does not appear in two rows. For "update", one stream can be in two
190      * rows and the materialization is done in UpdateResultSet. Note to future users of this
191      * class who may insert a stream into this temp holder: (1) As mentioned above, one
192      * un-materialized stream can't appear in two rows; you need to objectify it first otherwise.
193      * (2) If you need to retrieve a un-materialized stream more than once from the temp holder,
194      * you need to either materialize the stream the first time, or, if there's a memory constraint,
195      * in the first time create a RememberBytesInputStream with the byte holder being
196      * BackingStoreByteHolder, finish it, and reset it after usage.
197      * beetle 4896.
198      */

199     private ExecRow cloneRow(ExecRow inputRow)
200     {
201         DataValueDescriptor[] cols = inputRow.getRowArray();
202         int ncols = cols.length;
203         ExecRow cloned = ((ValueRow) inputRow).cloneMe();
204         for (int i = 0; i < ncols; i++)
205         {
206             if (cols[i] != null)
207             {
208                 /* Rows are 1-based, cols[] is 0-based */
209                 cloned.setColumn(i + 1, (DataValueDescriptor)((CloneableObject) cols[i]).cloneObject());
210             }
211         }
212         if (inputRow instanceof IndexValueRow)
213             return new IndexValueRow(cloned);
214         else
215             return cloned;
216     }
217
218     /**
219      * Insert a row
220      *
221      * @param inputRow the row to insert
222      *
223      * @exception StandardException on error
224      */

225     public void insert(ExecRow inputRow)
226         throws StandardException
227     {
228
229         if (SanityManager.DEBUG)
230         {
231             if(!isUniqueStream && !isVirtualMemHeap)
232                 SanityManager.ASSERT(state != STATE_DRAIN, "you cannot insert rows after starting to drain");
233         }
234         if (! isVirtualMemHeap)
235             state = STATE_INSERT;
236
237         if(uniqueIndexCreated)
238         {
239             if(isRowAlreadyExist(inputRow))
240                 return;
241         }
242
243         numRowsIn++;
244
245         if (lastArraySlot + 1 < rowArray.length)
246         {
247             rowArray[++lastArraySlot] = cloneRow(inputRow);
248             
249             //In case of unique stream we push every thing into the
250
// conglomerates for time being, we keep one row in the array for
251
// the template.
252
if(!isUniqueStream)
253                 return;
254         }
255             
256         if (!conglomCreated)
257         {
258             TransactionController tc = activation.getTransactionController();
259
260             /*
261             ** Create the conglomerate with the template row.
262             */

263             CID = tc.createConglomerate("heap",
264                                         inputRow.getRowArray(),
265                                         null, //column sort order - not required for heap
266
properties,
267                                         TransactionController.IS_TEMPORARY |
268                                         TransactionController.IS_KEPT);
269             conglomCreated = true;
270
271             cc = tc.openConglomerate(CID,
272                                 false,
273                                 TransactionController.OPENMODE_FORUPDATE,
274                                 TransactionController.MODE_TABLE,
275                                 TransactionController.ISOLATION_SERIALIZABLE);
276             if(isUniqueStream)
277                destRowLocation = cc.newRowLocationTemplate();
278
279         }
280
281         int status = 0;
282         if(isUniqueStream)
283         {
284             cc.insertAndFetchLocation(inputRow.getRowArray(), destRowLocation);
285             insertToPositionIndex(numRowsIn -1, destRowLocation);
286             //create the unique index based on input row ROW Location
287
if(!uniqueIndexCreated)
288                 isRowAlreadyExist(inputRow);
289
290         }else
291         {
292             status = cc.insert(inputRow.getRowArray());
293             if (isVirtualMemHeap)
294                 state = STATE_INSERT;
295         }
296
297         if (SanityManager.DEBUG)
298         {
299             if (status != 0)
300             {
301                 SanityManager.THROWASSERT("got funky status ("+status+") back from "+
302                         "ConglomerateConstroller.insert()");
303             }
304         }
305     }
306
307
308     /**
309      * Maintain an unique index based on the input row's row location in the
310      * base table, this index make sures that we don't insert duplicate rows
311      * into the temporary heap.
312      * @param inputRow the row we are inserting to temporary row holder
313      * @exception StandardException on error
314      */

315
316
317     private boolean isRowAlreadyExist(ExecRow inputRow) throws StandardException
318     {
319         DataValueDescriptor rlColumn;
320         RowLocation baseRowLocation;
321         rlColumn = inputRow.getColumn(inputRow.nColumns());
322
323         if(CID!=0 && rlColumn instanceof SQLRef)
324         {
325             baseRowLocation =
326                 (RowLocation) (rlColumn).getObject();
327         
328             if(!uniqueIndexCreated)
329             {
330                 TransactionController tc =
331                     activation.getTransactionController();
332                 int numKeys = 2;
333                 uniqueIndexRow = new DataValueDescriptor[numKeys];
334                 uniqueIndexRow[0] = baseRowLocation;
335                 uniqueIndexRow[1] = baseRowLocation;
336                 Properties JavaDoc props = makeIndexProperties(uniqueIndexRow, CID);
337                 uniqueIndexConglomId =
338                     tc.createConglomerate("BTREE",uniqueIndexRow , null, props,
339                                           TransactionController.IS_TEMPORARY |
340                                           TransactionController.IS_KEPT);
341                 uniqueIndex_cc = tc.openConglomerate(
342                                 uniqueIndexConglomId,
343                                 false,
344                                 TransactionController.OPENMODE_FORUPDATE,
345                                 TransactionController.MODE_TABLE,
346                                 TransactionController.ISOLATION_SERIALIZABLE);
347                 uniqueIndexCreated = true;
348             }
349
350             uniqueIndexRow[0] = baseRowLocation;
351             uniqueIndexRow[1] = baseRowLocation;
352             // Insert the row into the secondary index.
353
int status;
354             if ((status = uniqueIndex_cc.insert(uniqueIndexRow))!= 0)
355             {
356                 if(status == ConglomerateController.ROWISDUPLICATE)
357                 {
358                     return true ; // okay; we don't insert duplicates
359
}
360                 else
361                 {
362                     if (SanityManager.DEBUG)
363                     {
364                         if (status != 0)
365                         {
366                             SanityManager.THROWASSERT("got funky status ("+status+") back from "+
367                                                       "Unique Index insert()");
368                         }
369                     }
370                 }
371             }
372         }
373
374         return false;
375     }
376
377
378     /**
379      * Maintain an index that will allow us to read from the
380      * temporary heap in the order we inserted.
381      * @param position - the number of the row we are inserting into heap
382      * @param rl the row to Location in the temporary heap
383      * @exception StandardException on error
384      */

385
386     private void insertToPositionIndex(int position, RowLocation rl ) throws StandardException
387     {
388         if(!positionIndexCreated)
389         {
390             TransactionController tc = activation.getTransactionController();
391             int numKeys = 2;
392             position_sqllong = new SQLLongint();
393             positionIndexRow = new DataValueDescriptor[numKeys];
394             positionIndexRow[0] = position_sqllong;
395             positionIndexRow[1] = rl;
396             Properties JavaDoc props = makeIndexProperties(positionIndexRow, CID);
397             positionIndexConglomId =
398                 tc.createConglomerate("BTREE", positionIndexRow, null, props,
399                                       TransactionController.IS_TEMPORARY |
400                                       TransactionController.IS_KEPT);
401             positionIndex_cc = tc.openConglomerate(
402                                                     positionIndexConglomId,
403                                                     false,
404                                                     TransactionController.OPENMODE_FORUPDATE,
405                                                     TransactionController.MODE_TABLE,
406                                                     TransactionController.ISOLATION_SERIALIZABLE);
407             positionIndexCreated = true;
408         }
409         
410         position_sqllong.setValue(position);
411         positionIndexRow[0] = position_sqllong;
412         positionIndexRow[1] = rl;
413         //insert the row location to position index
414
positionIndex_cc.insert(positionIndexRow);
415     }
416
417     /**
418      * Get a result set for scanning what has been inserted
419      * so far.
420      *
421      * @return a result set to use
422      */

423     public CursorResultSet getResultSet()
424     {
425         state = STATE_DRAIN;
426         TransactionController tc = activation.getTransactionController();
427         if(isUniqueStream)
428         {
429             return new TemporaryRowHolderResultSet(tc, rowArray,
430                                                    resultDescription, isVirtualMemHeap,
431                                                    true, positionIndexConglomId, this);
432         }
433         else
434         {
435             return new TemporaryRowHolderResultSet(tc, rowArray, resultDescription, isVirtualMemHeap, this);
436
437         }
438     }
439
440     /**
441      * Purge the row holder of all its rows.
442      * Resets the row holder so that it can
443      * accept new inserts. A cheap way to
444      * recycle a row holder.
445      *
446      * @exception StandardException on error
447      */

448     public void truncate() throws StandardException
449     {
450         close();
451
452         for (int i = 0; i < rowArray.length; i++)
453         {
454             rowArray[i] = null;
455         }
456         lastArraySlot = -1;
457         numRowsIn = 0;
458         state = STATE_UNINIT;
459
460         /*
461         ** We are not expecting this to be called
462         ** when we have a temporary conglomerate
463         ** but just to be on the safe side, drop
464         ** it. We'd like do something cheaper,
465         ** but there is no truncate on congloms.
466         */

467         if (conglomCreated)
468         {
469             TransactionController tc = activation.getTransactionController();
470             tc.dropConglomerate(CID);
471             conglomCreated = false;
472         }
473     }
474
475     public long getTemporaryConglomId()
476     {
477         return CID;
478     }
479
480     public long getPositionIndexConglomId()
481     {
482         return positionIndexConglomId;
483     }
484
485
486
487     private Properties JavaDoc makeIndexProperties(DataValueDescriptor[]
488                                                indexRowArray, long conglomId ) throws StandardException {
489         int nCols = indexRowArray.length;
490         Properties JavaDoc props = new Properties JavaDoc();
491         props.put("allowDuplicates", "false");
492         // all columns form the key, (currently) required
493
props.put("nKeyFields", String.valueOf(nCols));
494         props.put("nUniqueColumns", String.valueOf(nCols-1));
495         props.put("rowLocationColumn", String.valueOf(nCols-1));
496         props.put("baseConglomerateId", String.valueOf(conglomId));
497         return props;
498     }
499
500     public void setRowHolderTypeToUniqueStream()
501     {
502         isUniqueStream = true;
503     }
504
505     /**
506      * Clean up
507      *
508      * @exception StandardException on error
509      */

510     public void close() throws StandardException
511     {
512         if (scan != null)
513         {
514             scan.close();
515             scan = null;
516         }
517
518         if (cc != null)
519         {
520             cc.close();
521             cc = null;
522         }
523
524         if (uniqueIndex_cc != null)
525         {
526             uniqueIndex_cc.close();
527             uniqueIndex_cc = null;
528         }
529
530         if (positionIndex_cc != null)
531         {
532             positionIndex_cc.close();
533             positionIndex_cc = null;
534         }
535
536         TransactionController tc = activation.getTransactionController();
537
538         if (uniqueIndexCreated)
539         {
540             tc.dropConglomerate(uniqueIndexConglomId);
541             uniqueIndexCreated = false;
542         }
543
544         if (positionIndexCreated)
545         {
546             tc.dropConglomerate(positionIndexConglomId);
547             uniqueIndexCreated = false;
548         }
549
550         if (conglomCreated)
551         {
552             tc.dropConglomerate(CID);
553             conglomCreated = false;
554         }
555
556         state = STATE_UNINIT;
557         lastArraySlot = -1;
558     }
559 }
560
561
Popular Tags