Oracle C++ Call Interface Programmer's Guide Release 2 (9.2) Part Number A96583-01 |
|
The following topics are covered in this chapter:
Oracle C++ Call Interface (OCCI) includes classes and methods for performing operations on large objects (LOBs). LOBs are either internal or external depending on their location with respect to the database.
Internal LOBs are stored inside database tablespaces in a way that optimizes space and enables efficient access. Internal LOBs use copy semantics and participate in the transactional model of the server. You can recover internal LOB
s in the event of transaction or media failure, and any changes to an internal LOB
value can be committed or rolled back. In other words, all the ACIDFoot 1 properties that pertain to using database objects also pertain to using internal LOBs.
There are three SQL datatypes for defining instances of internal LOBs:
Internal LOBs, whether persistent or temporary, use copy semantics. When you insert or update a LOB with a LOB from another row in the same table, the LOB value is copied so that each row has a copy of the LOB value.
The use of copy semantics results in both the LOB locator and the LOB value being copied, not just the LOB locator.
Internal LOBs are divided into persistent LOBs and temporary LOBs.
External LOB
s (BFILES
) are large binary data objects stored in operating system files outside database tablespaces. These files use reference semantics. Apart from conventional secondary storage devices such as hard disks, BFILEs may also be located on tertiary block storage devices such as CD-ROMs, PhotoCDs and DVDs.
The BFILE
datatype allows read-only byte stream access to large files on the file system of the database server.
Oracle can access BFILE
s if the underlying server operating system supports stream mode access to these operating system files.
There is one SQL datatype for declaring instances of external LOBs, called a BFILE. A BFILE is a LOB
whose value is composed of binary (raw) data and is stored outside the database tablespaces in a server-side operating system file.
External LOBs (BFILEs) use reference semantics. When a BFILE associated with a column of a row in a table is copied to another column, only the BFILE locator is copied, not the actual operating system file that houses the BFILE.
The size of the LOB value, among other things, dictates where it is stored. The LOB value is either stored inline with the row data or outside the row.
A LOB locator is stored inline with the row data and indicates where the LOB value is stored.
Data stored in a LOB
is termed the LOB
value. The value of an internal LOB
may or may not be stored inline with the other row data. If you do not set DISABLE STORAGE IN ROW
, and if the internal LOB
value is less than approximately 4,000 bytes, then the value is stored inline.Otherwise, it is stored outside the row. Since LOB
s are intended to be large objects, inline storage will only be relevant if your application mixes small and large LOB
s.
The LOB
value is automatically moved out of the row once it extends beyond approximately 4,000 bytes.
Regardless of where the value of the internal LOB
is stored, a LOB locator is stored in the row. You can think of a LOB
locator as a pointer to the actual location of the LOB
value. A LOB locator is a locator to an internal LOB
while a BFILE locator is a locator to an external LOB
.
LOB
s, the LOB
column stores a locator to the LOB value stored in a database tablespace. Each internal LOB
column and attribute for a given row has its own unique LOB
locator and a distinct copy of the LOB
value stored in the database tablespace.The classes and methods in Table 5-1are available for LOB operations.
Class | Method | Purpose |
---|---|---|
To access data in external LOBs (BFILEs) |
||
To manipulate internal LOB (BLOB) values and locators |
||
To manipulate internal LOB (CLOB and NCLOB) values and locators |
See Also:
Chapter 8, "OCCI Classes and Methods" for detailed information about each class and method. |
To create an internal or external LOB, initialize a new LOB locator in the database. Based on the type of LOB you want to create, use one of the following classes:
You can then use the related methods, as appropriate, to access the LOB value.
For any LOB write command to be successful, a transaction must be open. This means that if you commit a transaction before writing the data, then you must lock the row again (by reissuing the SELECT ... FOR UPDATE
statement, for example), because the COMMIT
closes the transaction.
OCCI provides methods to explicitly open and close internal and external LOBs:
Additional methods are available to check whether a particular LOB is already open:
These methods allow an OCCI application to mark the beginning and end of a series of LOB operations so that specific processing (for example, updating indexes, and so on) can be performed when a LOB is closed.
If an application does not wrap LOB operations between a set of open()
and close()
method calls, then each modification to the LOB implicitly opens and closes the LOB, thereby firing any triggers associated with changes to the LOB.
The LOB opening and closing mechanism has the following restrictions:
A LOB opened when there is no transaction must be closed before the end of session. If there are LOBs open at the end of session, then the openness is discarded and no triggers of extensible indexes are fired.
OCCI provides methods for reading and writing LOBS. For nonstreamed reads and writes, the following methods are used:
For streamed reads and writes, the following methods are used:
Bfile::getStream()
Blob::getChunkSize()
, Blob::getStream()
, and Blob::writeChunk()
Clob::getChunkSize()
, Clob::getStream()
, and Clob::writeChunk()
The remainder of this section provides code examples for streamed and unstreamed reads and writes.
The following code example demonstrates how to obtain data from an internal LOB (in this example, a BLOB) that is not null by using a nonstreamed read:
ResultSet *rset=stmt->executeQuery("SELECT ad_composite FROM print_media WHERE product_id=6666"); while(rset->next()) { Blob blob=rset->getBlob(1); if(blob.isNull()) cerr <<"Null Blob"<<endl; else { blob.open(OCCI_LOB_READONLY); const unsigned int BUFSIZE=100; char buffer[BUFSIZE]; unsigned int readAmt=BUFSIZE; unsigned int offset=1; //reading readAmt bytes from offset 1 blob.read(readAmt,buffer,BUFSIZE,offset); //process information in buffer . . . blob.close(); } } stmt->closeResultSet(rset);
Reading all information from a BLOB without using streams, as in the preceding code example, requires that you keep track of the read offset and the amount remaining to be read, and pass these values to the read()
method.
The following code example demonstrates how to read data from a BFILE, where the BFILE locator is not null, by using a nonstreamed read:
ResultSet *rset=stmt->executeQuery("SELECT ad_graphic FROM print_media WHERE product_id=6666"); while(rset->next()) { Bfile file=rset->getBfile(1); if(bfile.isNull()) cerr <<"Null Bfile"<<endl; else { //display the directory alias and the file name of the BFILE cout <<"File Name:"<<bfile.getFileName()<<endl; cout <<"Directory Alias:"<<bfile.getDirAlias()<<endl; if(bfile.fileExists()) { unsigned int length=bfile.length(); char *buffer=new char[length]; bfile.read(length, buffer, length, 1); //read all the contents of the BFILE into buffer, then process . . . delete[] buffer; } else cerr <<"File does not exist"<<endl; } } stmt->closeResultSet(rset);
The following code example demonstrates how to write data to an internal LOB (in this example, a BLOB) that is not null by using a nonstreamed write:
ResultSet *rset=stmt->executeQuery("SELECT ad_composite FROM print_media WHERE product_id=6666 FOR UPDATE"); while(rset->next()) { Blob blob=rset->getBlob(1); if(blob.isNull()) cerr <<"Null Blob"<<endl; else { blob.open(OCCI_LOB_READWRITE); const unsigned int BUFSIZE=100; char buffer[BUFSIZE]; unsigned int writeAmt=BUFSIZE; unsigned int offset=1; //writing writeAmt bytes from offset 1 //contents of buffer are replaced after each writeChunk(), //typically with an fread() while(<fread "BUFSIZE" bytes into buffer succeeds>) { blob.writeChunk(writeAmt, buffer, BUFSIZE, offset); offset += writeAmt; } blob.writeChunk(<remaining amt>, buffer, BUFSIZE, offset); blob.close(); } } stmt->closeResultSet(rset); conn->commit();
In the preceding code example, the writeChunk()
method is enclosed by the open()
and close()
methods. The writeChunk()
method operates on a LOB that is currently open and ensures that triggers do not fire for every chunk read. The write()
method can be used in place of the writeChunk()
method in the preceding example; however, the write()
method implicitly opens and closes the LOB.
The following code example demonstrates how to obtain data from an internal LOB (in this example, a BLOB) that is already populated by using a streamed read:
ResultSet *rset=stmt->executeQuery("SELECT ad_composite FROM print_media WHERE product_id=6666"); while(rset->next()) { Blob blob=rset->getBlob(1); if(blob.isNull()) cerr <<"Null Blob"<<endl; else { Stream *instream=blob.getStream(1,0); //reading from offset 1 to the end of the BLOB unsigned int size=blob.getChunkSize(); char *buffer=new char[size]; while((unsigned int length=instream->readBuffer(buffer,size))!=-1) { //process "length" bytes read into buffer . . . } delete[] buffer; blob.closeStream(instream); } } stmt->closeResultSet(rset);
The following code example demonstrates how to write data to an internal LOB (in this example, a BLOB) that is already populated by using a streamed write:
ResultSet *rset=stmt->executeQuery("SELECT ad_composite FROM print_media WHERE product_id=6666 FOR UPDATE"); while(rset->next()) { Blob blob=rset->getBlob(1); if(blob.isNull()) cerr <<"Null Blob"<<endl; else { char buffer[BUFSIZE]; Stream *outstream=blob.getStream(1,0); //writing from buffer beginning at offset 1 until //a writeLastBuffer() method is issued. //contents of buffer are replaced after each writeBuffer(), //typically with an fread() while(<fread "BUFSIZE" bytes into buffer succeeds>) ostream->writeBuffer(buffer,BUFSIZE); ostream->writeLastBuffer(buffer,<remaining amt>); blob.closeStream(outstream); } } stmt->closeResultSet(rset); conn->commit();
Read and write performance of internal LOBs can be improved by using either of the following methods:
Take advantage of the getChunkSize()
methods of the Blob
and Clob
classes to improve the performance of internal LOB read and write operations. The getChunkSize()
method returns the usable chunk size in bytes for BLOBs
and in characters for CLOBs
and NCLOBs
. When a read or write is done by using data whose size is a multiple of the usable chunk size and starts on a chunk boundary, performance improves. You can specify the chunk size for a LOB column when you create a table that contains the LOB.
Calling the getChunkSize()
method returns the usable chunk size of the LOB. An application can batch a series of write operations until an entire chunk can be written, rather than issuing multiple LOB write calls that operate on the same chunk.
To read through the end of a LOB, use the read()
method with an amount of 4 GB. This avoids the round-trip involved with first calling the getLength()
method
because the read()
method with an amount of 4 GB reads until the end of the LOB is reached.
Note: For LOBs which store varying width characters, the |
OCCI provides a shortcut to make it more efficient to write data to the end of a LOB. The writeAppend()
methods of the Blob
and Clob
classes enables an application to append data to the end of a LOB without first requiring a call to the getLength()
method to determine the starting point for a call to the write()
method.
The following code example demonstrates how to update an internal LOB (in this example, a CLOB) to empty:
Clob clob(conn); clob.setEmpty(); stmt->setSQL("UPDATE print_media SET ad_composite = :1 WHERE product_id=6666"); stmt->setClob(1, clob); stmt->executeUpdate(); conn->commit();
The following code example demonstrates how to update a BFILE:
Bfile bfile(conn); bfile.setName("MEDIA_DIR", "img1.jpg"); stmt->setSQL("UPDATE print_media SET ad_graphic = :1 WHERE product_id=6666"); stmt->setBfile(1, bfile); stmt->executeUpdate(); conn->commit();
An OCCI application can use the overloaded operator new()
to create a persistent or transient object with a LOB attribute. By default, all LOB attributes are constructed by using the default constructor and initialized to null.
It is possible to use OCCI to create a new persistent object with a LOB attribute. To do so, follow these steps:
Person *p=new(conn,"PERSON_TAB")Person();
Blob
object to empty.
p->imgBlob = Blob(conn); p->imgBlob.setEmpty();
If appropriate, then use the corresponding methods (set
xxx
methods and get
xxx
methods) on the Person
object to accomplish the same thing.
Blob
object as dirty.
p->markModified();
p->flush();
Ref<Person> r = p->getRef(); delete p; p = r.ptr();
p->imgBlob.write( ... );
To create a persistent object with BFILE attributes, follow these steps:
Person *p=new(conn,"PERSON_TAB")Person();
Bfile
object to empty.
p->imgBfile = Bfile(conn); p->setName(<Directory Alias>,<File Name>);
p->markModified();
p->flush();
p->imgBfile.read( ... );
An application can call the overloaded new()
method and create a transient object with an internal LOB (BLOB
, CLOB
, NCLOB
) attribute. However, you cannot perform any operations (for example, read or write) on the LOB attribute because transient LOBs are not currently supported. Calling the overloaded new()
method to create a transient internal LOB type does not fail, but the application cannot use any LOB operations with the transient LOB.
An application can, however, create a transient object with a FILE attribute and use the FILE attribute to read data from the file stored in the server's file system. The application can also call the overloaded new()
method to create a transient FILE and use that FILE to read from the server's file.
1 ACID = Access Control Information Directory. This is the attribute that determines who has what type of access and to what directory data. It contains a set of rules for structural and content access items. For more information, refer to the Oracle Internet Directory Administrator's Guide.
|
Copyright © 2001, 2002 Oracle Corporation. All Rights Reserved. |
|