The libpgtcl library provides a number of procedures that you can use to interact with PostgreSQL large-objects. A large-object is a value that is stored indirectly. When you create a column that will contain a large-object, the column should be of type OID (object-id). When you import a large-object into your database, the bits that make up the object are stored in the pg_largeobject system table and a reference is stored in your table. Large-objects are typically used to hold images, sound files, or large pieces of unstructured data.
There are two ways to create a large-object. First, you can create a large-object using the pg_lo_creat procedure. pg_lo_creat creates a new (empty) entry in the pg_largeobject table and returns the OID of that entry. After you have an empty large-object, you can write data into it using pg_lo_write.
Second, you can import an existing file (such as a JPEG-encoded photograph) into a database using pg_lo_import. The pg_lo_import manual page says that pg_lo_import requires two parameters (a connection handle and a filename) and returns nothing. That documentation is incorrect: pg_lo_import returns the OID of the new large-object.
Here is a code snippet that shows how to use the pg_lo_import procedure:
... pg_result [pg_exec $conn "BEGIN WORK"] -clear # Start a transaction set large_object_oid [pg_lo_import $conn "/images/happyface.jpg"] pg_result [pg_exec $conn "COMMIT WORK"] -clear ...
Note that you must call pg_lo_import within a transaction block. In fact, all large-object operations must occur within a transaction block.
The inverse of pg_lo_import is pg_lo_export. pg_lo_export copies a large-object into a file:
... pg_result [pg_exec $conn "BEGIN WORK"] -clear # Start a transaction pg_log_export $conn $large_object_oid "/images/jocularface.jpg" pg_result [pg_exec $conn "COMMIT WORK"] -clear ...
Like pg_lo_import, pg_lo_export must be called within a transaction block. You can also read the contents of a large-object using libpgtcl. To start with, you must open the desired large-object using pg_lo_open:
... set fd [pg_lo_open $conn $large_object_oid "rw"] ...
When you call pg_lo_open, you provide a connection handle, the OID of the large-object that you want, and an access mode. libpgtcl is a little fickle when it comes to large-object access modes; pg_lo_open expects “r”, “w,” or “rw,” but pg_lo_create expects “INV_READ,” “INV_WRITE,” or “INV_READ|INV_WRITE.” The value returned by pg_lo_open is a large-object handle; and after you have one of those, you can read from, write to, or move around in the large-object.
First, let's talk about positioning within a large-object. Large-objects can be, well, large. Your application may not need to read (or write) an entire large-object all at once; for really big large-objects, you may want to work with small chunks. To make this possible, libpgtcl lets you seek your large-object handle to the part that you are interested in; then, you can read or write from there.
The pg_lo_lseek procedure is modeled after the Unix lseek() function. pg_lo_lseek requires three parameters:
pg_lo_lseek connection-handle large-object-handle offset starting-point
The connection-handle and large-object-handle parameters are self-explanatory. offset specifies the number of bytes you want to move. starting-point specifies which position you want to move from. SEEK_CUR means that you want to move offset bytes relative to the current position. SEEK_SET means that you want to move offset bytes relative to the start of the object. SEEK_END will position your offset bytes from the end of the object.
If you specify a starting-point of SEEK_CUR or SEEK_END, offset can be either positive or negative (a negative offset moves you toward the beginning of the object). With SEEK_SET, offset should always be a non-negative number. A starting-point of SEEK_SET and an offset of 0 position you to the beginning of the object. A starting-point of SEEK_END and an offset of 0 position you to the end of the object. If you specify a starting-point of SEEK_CUR and an offset of 0, your position within the object remains unchanged.
The pg_lo_tell procedure returns your current position within an object. pg_lo_tell requires two parameters:
set current_offset [pg_lo_tell connection-handle large-object-handle]
You can determine the number of bytes in a large-object by seeking to the end of the object and then finding the offset:
... pg_lo_lseek $conn $object_handle 0 SEEK_CUR set object_size [pg_lo_tell $conn $object_handle] ...
After you have established a position within a large-object, you can read from or write to the object. To write (or modify) data in a large-object, use the pg_lo_write procedure:
pg_lo_write connection-handle large-object-handle string length
For example, if you want to append a file onto an existing large-object, you would write code similar to this:
... pg_exec $conn "BEGIN" set fd [open "/images/sadface.jpg"] set object_handle [pg_lo_open $conn $large_object_oid "rw"] pg_lo_lseek $conn $object_handle 0 SEEK_END while { [eof $fd] != 1 } { set val [read $fd 1000] pg_lo_write $conn $object_handle val [string length $val] } close $fd pg_lo_close $object_handle pg_exec $conn "COMMIT" ...
After opening the file and the large-object, seek to the end of the large-object and then copy from the file handle to the large-object handle, 1000 bytes at a time. We've also called pg_lo_close to close the large-object handle.
When you write to a large-object, you can create holes in the data. For example, if you start out with an empty large-object and then seek 100 bytes into it before calling pg_lo_write, you are creating a 100-byte hole at the beginning of the large-object. Holes are treated as if they contain zeroes. In other words, when you read back this particular large-object, the first 100 bytes will contain nothing but zeroes[5].
[5] In case you are wondering, PostgreSQL stores each large-object in a collection of blocks. Each block is typically 2048 bytes long. When you create a hole in a large-object, PostgreSQL will store the minimal number of blocks required to hold the object. If a block within a large-object contains nothing but a hole, it will not take up any physical space in the pg_largeobject table.
You can also read from a large-object in a piece-by-piece manner using pg_lo_lseek and pg_lo_read:
... pg_exec $conn "BEGIN" set object_handle [pg_lo_open $conn $large_object_oid "r"] pg_lo_lseek $conn $object_handle 0 SEEK_END set len [pg_tell $conn $object_handle] pg_lo_lseek $conn $object_handle 0 SEEK_SET pg_lo_read $conn $object_handle img $len image create photo my_photo my_photo put $img -format gif pg_lo_close $object_handle pg_exec $conn "COMMIT" ...
As before, you must start a transaction block before using any of the large-object procedures. After opening the large-object (using pg_lo_open), compute the size of the object. The easiest way to find the size of an existing large-object is to seek to the end and then use pg_lo_tell to find the offset of the last byte. After you know the size, you can read the entire object into a string variable using pg_lo_read. In the preceding example, we read the entire large-object in one call to pg_lo_read, but that is not strictly necessary. You can use pg_lo_lseek to move around within the large-object before you read (or write).
One important point here: When you call pg_lo_read (or pg_lo_write), your position within the object is advanced by the number of bytes read (or written).
The pg_lo_read procedure requires four parameters:
pg_lo_read connection-handle object-handle varname length
The connection-handle and object-handle parameters should be familiar by now. The varname parameter should contain the name of a variable—be careful with this parameter: You don't want to pass the contents of a variable; you want to pass the name. So, the following example will usually be incorrect:
pg_lo_read $conn $object_handle $img $len
This is likely to be wrong because you are passing the contents of the $img variable, not the name. You most likely want[6]
[6] The only time you would want to pass the value of a variable (as the third parameter) would be when one variable holds the name of another.
pg_lo_read $conn $object_handle img $len
There is one more large-object procedure that you might need to know about. If you want to remove a large-object from your database, use the pg_lo_unlink procedure:
pg_unlink $conn $large_object_id
18.216.255.250