Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/derby/impl/store/raw/data/StreamFileContainer.java


1   /*
2   
3      Derby - Class org.apache.derby.impl.store.raw.data.StreamFileContainer
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  
21  package org.apache.derby.impl.store.raw.data;
22  
23  import org.apache.derby.iapi.reference.SQLState;
24  
25  import org.apache.derby.iapi.services.context.ContextService;
26  
27  import org.apache.derby.iapi.services.sanity.SanityManager;
28  import org.apache.derby.iapi.services.io.Storable;
29  import org.apache.derby.iapi.services.io.StreamStorable;
30  import org.apache.derby.iapi.services.io.FormatIdInputStream;
31  import org.apache.derby.iapi.services.io.FormatIdOutputStream;
32  import org.apache.derby.iapi.services.io.FormatIdUtil;
33  import org.apache.derby.iapi.services.io.StoredFormatIds;
34  import org.apache.derby.iapi.services.io.TypedFormat;
35  import org.apache.derby.iapi.services.monitor.Monitor;
36  
37  import org.apache.derby.iapi.error.StandardException;
38  import org.apache.derby.iapi.store.access.AccessFactory;
39  import org.apache.derby.iapi.store.access.RowSource;
40  import org.apache.derby.iapi.store.access.RowUtil;
41  import org.apache.derby.iapi.store.access.TransactionController;
42  import org.apache.derby.iapi.store.raw.ContainerKey;
43  import org.apache.derby.iapi.store.raw.RawStoreFactory;
44  import org.apache.derby.iapi.store.raw.StreamContainerHandle;
45  
46  import org.apache.derby.io.StorageFactory;
47  import org.apache.derby.io.WritableStorageFactory;
48  import org.apache.derby.io.StorageFile;
49  
50  import org.apache.derby.impl.store.raw.data.DecryptInputStream;
51  import org.apache.derby.impl.store.raw.data.StoredFieldHeader;
52  import org.apache.derby.impl.store.raw.data.StoredRecordHeader;
53  
54  import org.apache.derby.iapi.services.io.ArrayInputStream;
55  import org.apache.derby.iapi.services.io.FormatableBitSet;
56  import org.apache.derby.iapi.services.io.CompressedNumber;
57  import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
58  import org.apache.derby.iapi.services.io.LimitInputStream;
59  import org.apache.derby.iapi.services.property.PropertyUtil;
60  
61  import java.util.Properties;
62  import java.io.InputStream;
63  import java.io.BufferedInputStream;
64  import java.io.OutputStream;
65  import java.io.IOException;
66  import java.io.EOFException;
67  import java.io.InvalidClassException;
68  import java.io.Externalizable;
69  
70  /**
71  
72    The format of this stream file is:
73    (RH) (FH) (field data) (FH) (field data) ........ (FH) (field data)
74  
75    Record header is stored once at the beginning of the file
76    for all the rows stored in this file.
77    Record Header indicates how many fields are in each row.
78    Then we just stored all the column from each row.
79    Field header stored on this file is fixed size with fieldDataLength
80    size set to LARGE_SLOT_SIZE (4) bytes.
81  
82    NOTE: No locks are used in this container.  All transaction are not logged.
83  
84  **/
85  
86  
87  public class StreamFileContainer implements TypedFormat
88  {
89  
90      /**************************************************************************
91       * Constant Fields of the class
92       **************************************************************************
93       */
94  
95    /*
96     * typed format
97     * format Id must fit in 4 bytes
98     */
99    protected static int formatIdInteger = 
100         StoredFormatIds.RAW_STORE_SINGLE_CONTAINER_STREAM_FILE; 
101 
102 
103     // 4 bytes for field data length
104   protected static final int LARGE_SLOT_SIZE = 4;  
105 
106   protected static final int MIN_BUFFER_SIZE = 
107         RawStoreFactory.STREAM_FILE_BUFFER_SIZE_MINIMUM;
108   protected static final int FIELD_STATUS = 
109         StoredFieldHeader.setFixed(StoredFieldHeader.setInitial(), true);
110   protected static final int FIELD_HEADER_SIZE = 
111         StoredFieldHeader.size(FIELD_STATUS, 0, LARGE_SLOT_SIZE);
112 
113 
114     /**************************************************************************
115      * Fields of the class
116      **************************************************************************
117      */
118   protected   ContainerKey          identity;
119   private   BaseDataFileFactory   dataFactory;  // the factory that made me
120 
121   private int                             bufferSize;
122 
123   private StorageFile file;
124 
125   private OutputStream                fileOut;
126   private DynamicByteArrayOutputStream    out;
127   private FormatIdOutputStream            logicalDataOut;
128 
129   private InputStream                 fileIn;
130   private BufferedInputStream             bufferedIn;
131   private DecryptInputStream              decryptIn;
132   private LimitInputStream                limitIn;
133   private FormatIdInputStream             logicalDataIn;
134 
135   private StoredRecordHeader              recordHeader;
136 
137   private byte[]                          ciphertext;
138   private byte[]                          zeroBytes;  // in case encryption
139                                                         // stream needs pad.
140 
141     /**************************************************************************
142      * Constructors for This class:
143      **************************************************************************
144      */
145 
146     /**
147      * Constructor.
148      *
149    * @exception  StandardException  Standard exception policy.
150      **/
151   StreamFileContainer(
152     ContainerKey        identity, 
153     BaseDataFileFactory dataFactory)
154     throws StandardException 
155     {
156     this.identity = identity;
157     this.dataFactory = dataFactory;
158   }
159 
160     /**
161      * Constructor
162      * <p>
163      * when rowSource is passed to the constructor, it will be loaded into the 
164      * container after the container has been created.
165      * <p>
166      *
167    * @exception  StandardException  Standard exception policy.
168      **/
169   StreamFileContainer(
170     ContainerKey        identity, 
171     BaseDataFileFactory dataFactory,
172     Properties          prop)
173     throws StandardException 
174     {
175     this.identity       = identity;
176     this.dataFactory    = dataFactory;
177 
178     try 
179         {
180       file = getFileName(identity, true, false);
181 
182             if (file.exists()) 
183             {
184         // note I'm left in the no-identity state as fillInIdentity()
185                 // hasn't been called.
186         throw StandardException.newException(
187                         SQLState.FILE_EXISTS, file);
188       }
189 
190       // get the properties to set buffer size
191       // derby.storage.streamFileBufferSize
192       getContainerProperties(prop);
193 
194     } 
195         catch (SecurityException se) 
196         {
197       throw StandardException.newException(
198                     SQLState.FILE_CREATE, se, file);
199     }
200   }
201 
202     /**************************************************************************
203      * Private/Protected methods of This class:
204      **************************************************************************
205      */
206 
207     /**
208      * Open a stream file container.
209      * <p>
210      * Open a container. Open the file that maps to this container, if the
211      * file does not exist then we assume the container was never created
212      * and return.
213      * If the file exists but we have trouble opening it then we throw some 
214      * exception.
215      * <p>
216      *
217    * @return The opened StreamFileContainer.
218      *
219      * @param forUpdate     Currently only accepts false, updating and existing
220      *                      stream file container is not currently supported.
221      *
222    * @exception  StandardException  Standard exception policy.
223      **/
224   protected StreamFileContainer open(boolean forUpdate) 
225         throws StandardException 
226     {
227 
228     file = getFileName(this.identity, false, true);
229         if (!file.exists())
230       return null;
231 
232     try 
233         {
234       if (!forUpdate) 
235             {
236         fileIn = file.getInputStream();
237 
238         if (dataFactory.databaseEncrypted()) 
239                 {
240           // if the database is encrypted, when reading the data back
241                     // from the file stream, we need to used the decrypt stream
242                     // to buffer up the bytes for reading.  DecryptInputStream 
243                     // also decrypts the data.
244 
245           MemByteHolder byteHolder = 
246                         new MemByteHolder(
247                             RawStoreFactory.STREAM_FILE_BUFFER_SIZE_DEFAULT);
248 
249           decryptIn = 
250                         new DecryptInputStream(fileIn, byteHolder, dataFactory);
251 
252           limitIn = new LimitInputStream(decryptIn);
253         } 
254                 else 
255                 {
256           bufferedIn = 
257                         new BufferedInputStream(
258                             fileIn, 
259                             RawStoreFactory.STREAM_FILE_BUFFER_SIZE_DEFAULT);
260 
261           limitIn = new LimitInputStream(bufferedIn);
262         }
263 
264         // the logicalDataIn input stream is on top of a limit Input
265         // stream, use a limit stream to make sure we don't read off
266         // more then what each column says it contains
267 
268         logicalDataIn = new FormatIdInputStream(limitIn);
269 
270         // get the record header
271         recordHeader = new StoredRecordHeader();
272         recordHeader.read(logicalDataIn);
273 
274       } 
275             else 
276             {
277         if (SanityManager.DEBUG)
278           SanityManager.THROWASSERT(
279                         "updating existing stream container not supported yet");
280 
281         return null;
282       }
283     } 
284         catch (IOException ioe) 
285         {
286       throw StandardException.newException(
287                     SQLState.FILE_CREATE, ioe, file);
288     }
289 
290     return this;
291   }
292 
293     /**
294      * Close the stream file.
295      * <p>
296      * Close this stream file, and all streams associated with it.
297      * <p>
298      *
299    * @exception  StandardException  Standard exception policy.
300      **/
301   protected void close()
302     {
303     try 
304         {
305 
306       if (fileIn != null) 
307             {
308         fileIn.close();
309         fileIn = null;
310         if (dataFactory.databaseEncrypted()) 
311                 {
312           decryptIn.close();
313           decryptIn = null;
314         } 
315                 else 
316                 {
317           bufferedIn.close();
318           bufferedIn = null;
319         }
320         logicalDataIn.close();
321         logicalDataIn = null;
322       }
323 
324       if (fileOut != null) 
325             {
326         fileOut.close();
327         logicalDataOut.close();
328         fileOut = null;
329         logicalDataOut = null;
330         out = null;
331       }
332 
333     } 
334         catch (IOException ioe) 
335         {
336             // ignore close errors from fileOut.close() and fileIn.close() - 
337             // there isn't much we can do about them anyway - and some of the
338             // interfaces don't want to deal with exceptions from close().
339 
340             /*
341       throw StandardException.newException(
342                     SQLState.FILE_CREATE, ioe, file);
343             */
344     }
345   }
346 
347     /**************************************************************************
348      * Public Methods of This class:
349      **************************************************************************
350      */
351 
352     /**
353      * Return my format identifier.
354      **/
355   public int getTypeFormatId() 
356     {
357     return StoredFormatIds.RAW_STORE_SINGLE_CONTAINER_STREAM_FILE;
358   }
359 
360     /**
361      * Request the system properties associated with a stream container. 
362      * <p>
363      * Request the value of properties associated with a stream container. 
364      * The following properties can be requested:
365      *     derby.storage.streamFileBufferSize
366    *
367      * <p>
368      * To get the value of a particular property add it to the property list,
369      * and on return the value of the property will be set to it's current 
370      * value.  For example:
371      *
372      * get_prop(ConglomerateController cc)
373      * {
374      *     Properties prop = new Properties();
375      *     prop.put("derby.storage.streamFileBufferSize", "");
376      *     cc.getContainerProperties(prop);
377      *
378      *     System.out.println(
379      *         "stream table's buffer size = " + 
380      *         prop.getProperty("derby.storage.streamFileBufferSize");
381      * }
382      *
383      * @param prop   Property list to fill in.
384      *
385    * @exception  StandardException  Standard exception policy.
386      **/
387     public void getContainerProperties(Properties prop) 
388         throws StandardException 
389     {
390 
391     AccessFactory af = (AccessFactory)
392       Monitor.getServiceModule(dataFactory, AccessFactory.MODULE);
393 
394     TransactionController tc = 
395             (af == null) ? 
396                 null : 
397                 af.getTransaction(
398                     ContextService.getFactory().getCurrentContextManager());
399 
400     bufferSize = 
401       PropertyUtil.getServiceInt(tc, prop,
402         RawStoreFactory.STREAM_FILE_BUFFER_SIZE_PARAMETER,  
403         RawStoreFactory.STREAM_FILE_BUFFER_SIZE_MINIMUM, 
404         RawStoreFactory.STREAM_FILE_BUFFER_SIZE_MAXIMUM,
405         RawStoreFactory.STREAM_FILE_BUFFER_SIZE_DEFAULT);
406   }
407 
408     /**
409      * Request the container key associated with the stream container. 
410    **/
411   public ContainerKey getIdentity() 
412     {
413     return this.identity;
414   }
415 
416     /**
417      * Can I use this container?
418      * <p>
419      * This method always return true right now.
420      * In the future when there are different uses for this container,
421      * we may need to add qualifications for this.
422      *
423    * @exception  StandardException  Standard exception policy.
424      **/
425   protected boolean use(StreamContainerHandle handle) 
426         throws StandardException 
427     {
428     return true;
429   }
430 
431     /**
432      * load data into this container.
433      * <p>
434      * populate the stream container with data in the rowSource
435      * <p>
436      *
437      * @param rowSource The row source to get rows to load into this container.
438      *
439    * @exception  StandardException  Standard exception policy.
440      **/
441   public void load(RowSource rowSource) 
442         throws StandardException 
443     {
444     // use this output stream to buffer rows before inserting into file.
445     out                 = new DynamicByteArrayOutputStream(bufferSize);
446     logicalDataOut      = new FormatIdOutputStream(out);
447     boolean encrypted   = dataFactory.databaseEncrypted();
448 
449     // reserve the first dataFactory.getEncryptionBlockSize() - 1 bytes, if the database is 
450         // encrypted These reserved bytes will be used to pad the byte array if
451         // it is not dataFactory.getEncryptionBlockSize() aligned.
452     if (encrypted) 
453         {
454       if (zeroBytes == null)
455         zeroBytes = new byte[dataFactory.getEncryptionBlockSize() - 1];
456 
457       out.write(zeroBytes, 0, dataFactory.getEncryptionBlockSize() - 1);
458     }
459 
460     try 
461         {
462       fileOut = file.getOutputStream();
463 
464       FormatableBitSet validColumns = rowSource.getValidColumns();
465 
466       Object[] row = rowSource.getNextRowFromRowSource();
467 
468       int numberFields = 0;
469       if (validColumns != null) 
470             {
471         for (int i = validColumns.getLength() - 1; i >= 0; i--) 
472                 {
473           if (validColumns.isSet(i)) 
474                     {
475             numberFields = i + 1;
476             break;
477           }
478         }
479       } 
480             else 
481             {
482         numberFields = row.length;
483       }
484 
485       // make the record header to have 0 record id
486       recordHeader = new StoredRecordHeader(0, numberFields);
487 
488       // write the record header once for all the rows, directly to the 
489             // beginning of the file.
490       int rhLen = recordHeader.write(out);
491 
492       int validColumnsSize = 
493                 validColumns == null ? 0 : validColumns.getLength();
494 
495       while (row != null) 
496             {
497 
498         int arrayPosition = -1;
499 
500         for (int i = 0; i < numberFields; i++) 
501                 {
502 
503           // write each column out
504           if (validColumns == null) 
505                     {
506             arrayPosition++;
507             Object column = row[arrayPosition];
508             writeColumn(column);
509           } 
510                     else 
511                     {
512 
513             if (validColumnsSize > i && validColumns.isSet(i)) 
514                         {
515               arrayPosition++;
516               Object column = row[arrayPosition];
517               writeColumn(column);
518             } 
519                         else 
520                         {
521               // it is a non-existent column
522               writeColumn(null);
523             }
524           }
525 
526           // put the buffer onto the page, only if it exceeded the 
527                     // original buffer size or it has less than 100 bytes left 
528                     // in the buffer
529           if ((out.getUsed() >= bufferSize) || 
530                         ((bufferSize - out.getUsed()) < MIN_BUFFER_SIZE)) 
531                     {
532             writeToFile();
533           }
534         }
535 
536         // get the next row and its valid columns from the rowSource
537         row = rowSource.getNextRowFromRowSource();
538       }
539 
540 
541       // Write the buffer to the file if there is something in the output
542       // buffer.  Remember we pad the output buffer with
543       // dataFactory.getEncryptionBlockSize() - 1 if this is an encypted database
544       if (encrypted)
545       {
546         if (out.getUsed() > (dataFactory.getEncryptionBlockSize() - 1))
547           writeToFile();
548       }
549       else if (out.getUsed() > 0) 
550             {
551         writeToFile();
552       }
553 
554     } 
555         catch (IOException ioe) 
556         {
557       // handle IO error...
558       throw StandardException.newException(
559                     SQLState.DATA_UNEXPECTED_EXCEPTION, ioe);
560 
561     }
562         finally 
563         {
564       close();
565     }
566   }
567 
568   /*
569 
570    */
571     /**
572      * Write the buffer to the file.
573      * <p>
574      * If the database is encrypted, the dataFactory.getEncryptionBlockSize() - 1 reserved bytes will
575      * be used to pad the byte array to be dataFactory.getEncryptionBlockSize()
576      * aligned.  Before the bytes are encrypted and written to the file stream,
577      * the actual length of the byte array is written out as a compressed 
578      * integer.  This number will be used when decrypting the data.
579      *
580      * If the database is not encrypted, then, we don't reserve the bytes 
581      * upfront, and we simple just write the bytes out to the file stream.
582      *
583    * @exception  StandardException  Standard exception policy.
584      **/
585   private void writeToFile() 
586         throws StandardException 
587     {
588 
589     try
590         {
591       if (dataFactory.databaseEncrypted()) 
592             {
593         // if db is encrypted, 
594                 // use the first ENCRYPTION_ALIGN bytes for padding.
595                 //
596         int realLen = out.getUsed() - (dataFactory.getEncryptionBlockSize() - 1);
597         int tail = realLen % dataFactory.getEncryptionBlockSize();
598         int padding = 
599                     (tail == 0) ? 0 : 
600                     (dataFactory.getEncryptionBlockSize() - tail);
601 
602         int startByte = (tail == 0) ? (dataFactory.getEncryptionBlockSize() - 1) : (tail - 1);
603         int encryptedLen = realLen + padding;
604 
605         // there is nothing to write, just the encryption padding
606         if (realLen <= 0)
607           return;
608 
609         if (ciphertext == null)
610                 {
611           ciphertext = new byte[encryptedLen];
612                 }
613         else 
614                 {
615           if (ciphertext.length < encryptedLen)
616             ciphertext = new byte[encryptedLen];
617         }
618 
619         dataFactory.encrypt(
620                     out.getByteArray(), startByte, encryptedLen, ciphertext, 0);
621 
622         // write out the actual length, then the encrypted bytes.
623         CompressedNumber.writeInt(fileOut, realLen);
624         dataFactory.writeInProgress();
625         try
626         {
627           fileOut.write(ciphertext, 0, encryptedLen);
628         }
629         finally
630         {
631           dataFactory.writeFinished();
632         }
633 
634         // reset the dynamic buffer
635         out.reset();
636 
637         // reserve bytes if database is encrypted.
638         if (dataFactory.databaseEncrypted())
639                 {
640           if (zeroBytes == null)
641             zeroBytes = new byte[dataFactory.getEncryptionBlockSize() - 1];
642 
643           out.write(zeroBytes, 0, dataFactory.getEncryptionBlockSize() - 1);
644         }
645 
646       } 
647             else 
648             {
649         // nothing to write
650         if (out.getUsed() == 0)
651           return;
652 
653         dataFactory.writeInProgress();
654         try
655         {
656           fileOut.write(out.getByteArray(), 0, out.getUsed());
657         }
658         finally
659         {
660           dataFactory.writeFinished();
661         }
662 
663         // reset the dynamic buffer
664         out.reset();
665       }
666     } 
667         catch (IOException ioe) 
668         {
669       throw StandardException.newException(
670                     SQLState.DATA_UNEXPECTED_EXCEPTION, ioe);
671     }
672   }
673 
674   private void writeColumn(Object column) 
675         throws StandardException, IOException 
676     {
677 
678     int fieldStatus = FIELD_STATUS;
679     if (column == null) 
680         {
681       // just write a non-existent header.
682       fieldStatus = StoredFieldHeader.setNonexistent(fieldStatus);
683       StoredFieldHeader.write(out, fieldStatus, 0, LARGE_SLOT_SIZE);
684       return;
685     }
686 
687     // if the column is a null column, write the field header now.
688     if (column instanceof Storable) 
689         {
690       Storable sColumn = (Storable) column;
691       if (sColumn.isNull()) 
692             {
693         fieldStatus = StoredFieldHeader.setNull(fieldStatus, true);
694         StoredFieldHeader.write(out, fieldStatus, 0, LARGE_SLOT_SIZE);
695         return;
696       }
697     }
698 
699     int beginPosition = out.getPosition();
700     int fieldDataLength = 0;
701 
702     // write out the header, mostly to reserve the space
703     StoredFieldHeader.write(
704             out, fieldStatus, fieldDataLength, LARGE_SLOT_SIZE);
705 
706     if (column instanceof StreamStorable) 
707         {
708       if (((StreamStorable) column).returnStream() != null) 
709             {
710         column = (InputStream) ((StreamStorable) column).returnStream();
711       }
712     }
713 
714     if (column instanceof InputStream) 
715         {
716       InputStream inColumn = (InputStream) column;
717       int bufferLen = inColumn.available();
718       byte[] bufData = new byte[bufferLen];
719 
720       do 
721             {
722         int lenRead = inColumn.read(bufData, bufferLen, 0);
723         if (lenRead != -1) 
724                 {
725           fieldDataLength += lenRead;
726           out.write(bufData, lenRead, 0);
727         } 
728                 else
729                 {
730           break; 
731                 }
732       } while (true);
733 
734     } 
735         else if (column instanceof Storable) 
736         {
737 
738       Storable sColumn = (Storable) column;
739       // write field data to the stream, we already handled the null case
740             
741       sColumn.writeExternal(logicalDataOut);
742       fieldDataLength = 
743                 out.getPosition() - beginPosition - FIELD_HEADER_SIZE;
744 
745     } 
746         else 
747         {
748       // Serializable/Externalizable/Formattable
749       // all look the same at this point.
750       logicalDataOut.writeObject(column);
751       fieldDataLength = 
752                 out.getPosition() - beginPosition - FIELD_HEADER_SIZE;
753     }
754 
755     // Now we go back to update the fieldDataLength in the field header
756     int endPosition = out.getPosition();
757     out.setPosition(beginPosition);
758 
759     StoredFieldHeader.write(
760             out, fieldStatus, fieldDataLength, LARGE_SLOT_SIZE);
761 
762     // set position to the end of the field
763     if (!StoredFieldHeader.isNull(fieldStatus))
764       out.setPosition(endPosition);
765   }
766 
767   public boolean fetchNext(Object[] row) 
768         throws StandardException 
769     {
770 
771     boolean inUserCode = false;
772     int columnId = 0;
773     
774     try  
775         {
776 
777       // Get the number of columns in the row.
778       int numberFields = recordHeader.getNumberFields();
779 
780       int arrayPosition = 0;
781       for (columnId = 0; columnId < numberFields; columnId++) 
782             {
783 
784         if (arrayPosition >= row.length)
785           break;
786   
787         limitIn.clearLimit();
788 
789         // read the field header
790         int fieldStatus = StoredFieldHeader.readStatus(logicalDataIn);
791         int fieldDataLength = StoredFieldHeader.readFieldDataLength(
792           logicalDataIn, fieldStatus, LARGE_SLOT_SIZE);
793 
794         limitIn.setLimit(fieldDataLength);
795 
796         if (SanityManager.DEBUG) 
797                 {
798 
799                     if (StoredFieldHeader.isExtensible(fieldStatus))
800                     {
801                         SanityManager.THROWASSERT(
802                             "extensible fields not supported yet.  columnId = "
803                             + columnId);
804                     }
805 
806           SanityManager.ASSERT(!StoredFieldHeader.isOverflow(fieldStatus),
807             "overflow field is not supported yet");
808         }
809 
810         Object column = row[arrayPosition];
811         
812         // Deal with Storable columns
813         if (StoredFieldHeader.isNullable(fieldStatus)) 
814                 {
815                   
816           if (column == null)
817                     {
818             throw StandardException.newException(
819                                 SQLState.DATA_NULL_STORABLE_COLUMN, 
820                                 Integer.toString(columnId));
821                     }
822 
823           // SRW-DJD RESOLVE: - fix error message
824           if (!(column instanceof Storable)) 
825                     {
826             throw StandardException.newException(
827                             SQLState.DATA_NULL_STORABLE_COLUMN, 
828                             column.getClass().getName());
829           }
830 
831           Storable sColumn = (Storable) column;
832 
833           // is the column null ?
834           if (StoredFieldHeader.isNull(fieldStatus)) 
835                     {
836 
837             sColumn.restoreToNull();
838             arrayPosition++;
839             continue;
840           }
841 
842           inUserCode = true;
843           sColumn.readExternal(logicalDataIn);
844           inUserCode = false;
845           arrayPosition++;
846           continue;
847         }
848 
849         // Only Storables can be null ... SRW-DJD RESOLVE: - fix error message
850         if (StoredFieldHeader.isNull(fieldStatus))
851                 {
852           throw StandardException.newException(
853                         SQLState.DATA_NULL_STORABLE_COLUMN, 
854                         Integer.toString(columnId));
855                 }
856 
857         // This is a non-extensible field, which means the caller must 
858                 // know the correct type and thus the element in row is the 
859                 // correct type or null. If the element implements 
860                 // Externalizable then we can just fill it in, otherwise it 
861                 // must be Serializable and we have to throw it away.
862 
863         Object neColumn = row[arrayPosition];
864 
865         if (neColumn instanceof Externalizable) 
866                 {
867 
868           Externalizable exColumn = (Externalizable) neColumn;
869 
870           inUserCode = true;
871           exColumn.readExternal(logicalDataIn);
872           inUserCode = false;
873 
874           arrayPosition++;
875           continue;
876         }
877 
878         // neColumn will be ignored
879         neColumn = null;
880         inUserCode = true;
881         row[arrayPosition] = logicalDataIn.readObject();
882         inUserCode = false;
883 
884         arrayPosition++;
885         continue;
886       }
887 
888     } 
889         catch (IOException ioe) 
890         {
891 
892       // an exception during the restore of a user column, this doesn't
893       // make the databse corrupt, just that this field is inaccessable
894       if (inUserCode) 
895             { 
896 
897         if (ioe instanceof EOFException) 
898                 {
899           throw StandardException.newException(
900                         SQLState.DATA_STORABLE_READ_MISMATCH, 
901                         ioe, logicalDataIn.getErrorInfo());
902         }
903 
904         throw StandardException.newException(
905                     SQLState.DATA_STORABLE_READ_EXCEPTION, 
906                     ioe, logicalDataIn.getErrorInfo());
907       }
908 
909       if (ioe instanceof InvalidClassException)
910             {
911         throw StandardException.newException(
912                         SQLState.DATA_STORABLE_READ_EXCEPTION, 
913                         ioe, logicalDataIn.getErrorInfo());
914          }
915 
916       // If we are at the end of the file, trying to fetch the first 
917             // column, then we know there is no more rows to fetch
918       if ((ioe instanceof EOFException) && (columnId == 0)) 
919             {
920         close();
921         return false;
922       }
923 
924       throw dataFactory.markCorrupt(
925                 StandardException.newException(
926                     SQLState.DATA_CORRUPT_STREAM_CONTAINER, ioe, identity));
927 
928     } 
929         catch (ClassNotFoundException cnfe) 
930         {
931 
932       if (SanityManager.DEBUG) 
933             {
934         SanityManager.ASSERT(inUserCode);
935       }
936 
937       // an exception during the restore of a user column, this doesn't
938       // make the databse corrupt, just that this field is inaccessable
939       throw StandardException.newException(
940                 SQLState.DATA_STORABLE_READ_MISSING_CLASS, cnfe, 
941                 logicalDataIn.getErrorInfo());
942 
943     } 
944         catch (LinkageError le) 
945         {
946       if (inUserCode)
947             {
948         throw StandardException.newException(
949                     SQLState.DATA_STORABLE_READ_EXCEPTION, le,
950                     logicalDataIn.getErrorInfo());
951             }
952       throw le;
953     }
954 
955     return true;
956 
957   }
958 
959     /**
960      * Close the stream file and remove the file.
961      *
962      * @exception StandardException Segment directory cannot be created
963      **/
964   public boolean removeContainer() 
965         throws StandardException 
966     {
967     close();
968 
969         if (file.exists())
970         {
971             return file.delete();
972         }
973         else
974         {
975             return true;
976         }
977 
978 
979   }
980 
981     /**
982      * Return a file name for the identity.
983      * <p>
984      * Return a valid file name for the identity, or null if the data
985      * directory for this segment cannot be created
986      *
987    * @exception StandardException Segment directory cannot be created
988      **/
989   protected StorageFile getFileName(
990     ContainerKey    identity, 
991     boolean         forCreate, 
992     boolean         errorOK)
993      throws StandardException 
994     {
995     if (identity.getSegmentId() == StreamContainerHandle.TEMPORARY_SEGMENT)
996         {
997       return( dataFactory.storageFactory.newStorageFile( dataFactory.storageFactory.getTempDir(), 
998                     "T" + identity.getContainerId() + ".tmp"));
999         }
1000    else 
1001        {
1002      if (SanityManager.DEBUG)
1003        SanityManager.THROWASSERT(
1004                    "cannot create stream container in non-temp segments yet.");
1005
1006      StorageFile container = dataFactory.getContainerPath( identity, false);
1007
1008      if (!container.exists()) 
1009            {
1010
1011        if (!forCreate)
1012          return null;
1013
1014        StorageFile directory = container.getParentDir();
1015
1016        if (!directory.exists()) 
1017                {
1018          // make sure only 1 thread can create a segment at one time
1019          synchronized(dataFactory) 
1020                    {
1021            if (!directory.exists()) 
1022                        {
1023              if (!directory.mkdirs()) 
1024                            {
1025                if (errorOK)
1026                  return null;
1027                else
1028                  throw StandardException.newException(
1029                                            SQLState.FILE_CANNOT_CREATE_SEGMENT,
1030                                            directory);
1031              }
1032            }
1033          }
1034        }
1035      }
1036      return container;
1037    }
1038  }
1039}