LARGE

LARGE

labs Home Page Subroutines Index LastKnownRequestProcessed


DESCRIPTION
       In Postgres, data values are stored in tuples and individ-
       ual tuples cannot span data pages. Since  the  size  of  a
       data  page is 8192 bytes, the upper limit on the size of a
       data value is relatively low. To support  the  storage  of
       larger  atomic  values,  Postgres  provides a large object
       interface.  This interface provides  file-oriented  access
       to user data that has been declared to be a large type.

       This section describes the implementation and the program-
       matic and query  language  interfaces  to  Postgres  large
       object data.

Historical Note
       Originally, postgres 4.2 supports three standard implemen-
       tations of large objects: as files external  to  Postgres,
       as  Unix  files  managed  by  Postgres, and as data stored
       within the Postgres database. It causes considerable  con-
       fusion  among  users.  As  a result, we only support large
       objects as data stored within  the  Postgres  database  in
       Postgres.  Even though is is slower to access, it provides
       stricter data integrity and time  travel.  For  historical
       reasons, they are called Inversion large objects. (We will
       use Inversion and large objects  interchangeably  to  mean
       the same thing in this section.)

Inversion Large Objects
       The  Inversion  large  object  implementation breaks large
       objects up into "chunks" and stores the chunks  in  tuples
       in  the database.  A B-tree index guarantees fast searches
       for the correct chunk  number  when  doing  random  access
       reads and writes.

Large Object Interfaces
       The  facilities Postgres provides to access large objects,
       both in the backend as part of user-defined  functions  or
       the  front end as part of an application using the  inter-
       face, are described below. (For users familiar with  post-
       gres  4.2, Postgres has a new set of functions providing a
       more coherent interface. The interface  is  the  same  for
       dynamically-loaded C functions as well as for .

       The  Postgres  large object interface is modeled after the
       Unix file system interface, with analogues  of  open  (2),
       read  (2), write (2), lseek (2), etc.  User functions call
       these routines to retrieve only the data of interest  from
       a  large  object.   For  example,  if  a large object type
       called mugshot existed that stored photographs  of  faces,
       then  a function called beard could be declared on mugshot
       data.  Beard could look at the lower  third  of  a  photo-
       graph,  and determine the color of the beard that appeared
       there, if any.  The entire large object value need not  be
       buffered, or even examined, by the beard function.

       Large  objects  may  be accessed from dynamically-loaded C
       functions or database client programs that link the  Libpq
       library.  Postgres provides a set of routines that support
       opening, reading, writing, closing, and seeking  on  large
       objects.

Creating a Large Object
       The routine
       Oid lo_creat(PGconn *conn, int mode)
       creates a new large object. The mode is a bitmask describ-
       ing several different attributes of the new  object.   The
       symbolic constants listed here are defined in
       /usr/local/pgsql/src/backend/libpq/libpq-fs.h
       The access type (read, write, or both) is controlled by OR
       ing together the bits INV_READ and  INV_WRITE  .   If  the
       large  object  should be archived - that is, if historical
       versions of it should be moved periodically to  a  special
       archive relation - then the INV_ARCHIVE bit should be set.
       The low-order sixteen bits of mask are the storage manager
       number on which the large object should reside.  For sites
       other than Berkeley, these bits should always be zero.

       The commands below create an (Inversion) large object:
       inv_oid = lo_creat(INV_READ|INV_WRITE|INV_ARCHIVE);

Importing a Large Object
       To import a UNIX file as a large object, call
       Oid
       lo_import(PGconn *conn, text *filename)
       The filename argument specifies the UNIX pathname  of  the
       file to be imported as a large object.

Exporting a Large Object
       To export a large object into UNIX file, call
       int
       lo_export(PGconn *conn, Oid lobjId, text *filename)
       The  lobjId argument specifies the Oid of the large object
       to export and the filename  argument  specifies  the  UNIX
       pathname of the file.

Opening an Existing Large Object
       To open an existing large object, call
       int
       lo_open(PGconn *conn, Oid lobjId, int mode, ...)
       The  lobjId argument specifies the Oid of the large object
       to open.  The mode bits  control  whether  the  object  is
       opened for reading INV_READ ), ( writing INV_WRITE ), ( or
       both.

       A large object cannot be  opened  before  it  is  created.
       lo_open returns a large object descriptor for later use in
       lo_read , lo_write , lo_lseek , lo_tell , and lo_close .

Writing Data to a Large Object
       The routine
       int
       lo_write(PGconn *conn, int fd, char *buf, int len)
       writes len bytes from buf to large object  fd  .   The  fd
       argument must have been returned by a previous lo_open .

       The  number of bytes actually written is returned.  In the
       event of an error, the return value is negative.

Seeking on a Large Object
       To change the current read or write location  on  a  large
       object, call
       int
       lo_lseek(PGconn *conn, int fd, int offset, int whence)
       This  routine  moves  the current location pointer for the
       large object described by fd to the new location specified
       by  offset  .  The valid values for .I whence are SEEK_SET
       SEEK_CUR and SEEK_END.

Closing a Large Object Descriptor
       A large object may be closed by calling
       int
       lo_close(PGconn *conn, int fd)
       where fd is a large object descriptor returned by  lo_open
       .   On  success,  lo_close  returns  zero.   On error, the
       return value is negative.

Built in registered functions
       There are two built-in registered functions, lo_import and
       lo_export which are convenient for use in SQL queries.

       Here is an example of there use
       CREATE TABLE image (
               name            text,
               raster          oid
       );

       INSERT INTO image (name, raster)
          VALUES ('beautiful image', lo_import('/etc/motd'));

       SELECT lo_export(image.raster, '/tmp/motd') from image
          WHERE name = 'beautiful image';

Accessing Large Objects from LIBPQ
       Below is a sample program which shows how the large object
       interface in

        can be used.  Parts of the program are commented out  but
       are left in the source for the readers benefit.  This pro-
       gram can be found in
       ../src/test/examples

       Frontend applications which use the large object interface
       in .br

       should  include  the header file libpq/libpq-fs.h and link
       with the libpq library.

Sample Program
       /*-------------------------------------------------------------------------
        *
        * testlo.c--
        *    test using large objects with libpq
        *
        * Copyright (c) 1994, Regents of the University of California
        *
        *
        * IDENTIFICATION
        *    $Header: /usr/local/cvsroot/pgsql/src/man/large_objects.3,v 1.6 1998/03/23 15:09:29 momjian Exp $
        *
        *-------------------------------------------------------------------------
        */
       #include <stdio.h>
       #include "libpq-fe.h"
       #include "libpq/libpq-fs.h"

       #define BUFSIZE          1024

       /*
        * importFile -
        *    import file "in_filename" into database as large object "lobjOid"
        *
        */
       Oid importFile(PGconn *conn, char *filename)
       {
           Oid lobjId;
           int lobj_fd;
           char buf[BUFSIZE];
           int nbytes, tmp;
           int fd;

           /*
            * open the file to be read in
            */
           fd = open(filename, O_RDONLY, 0666);
           if (fd < 0)  {   /* error */
            fprintf(stderr, "can't open unix file\"%s\"\n", filename);
           }

           /*
            * create the large object
            */
           lobjId = lo_creat(conn, INV_READ|INV_WRITE);
           if (lobjId == 0) {
            fprintf(stderr, "can't create large object");
           }

           lobj_fd = lo_open(conn, lobjId, INV_WRITE);
           /*
            * read in from the Unix file and write to the inversion file
            */
           while ((nbytes = read(fd, buf, BUFSIZE)) > 0) {

            tmp = lo_write(conn, lobj_fd, buf, nbytes);
            if (tmp < nbytes) {
                fprintf(stderr, "error while reading \"%s\"", filename);
            }
           }

           (void) close(fd);
           (void) lo_close(conn, lobj_fd);

           return lobjId;
       }

       void pickout(PGconn *conn, Oid lobjId, int start, int len)
       {
           int lobj_fd;
           char* buf;
           int nbytes;
           int nread;

           lobj_fd = lo_open(conn, lobjId, INV_READ);
           if (lobj_fd < 0) {
            fprintf(stderr,"can't open large object %d",
                 lobjId);
           }

           lo_lseek(conn, lobj_fd, start, SEEK_SET);
           buf = malloc(len+1);

           nread = 0;
           while (len - nread > 0) {
            nbytes = lo_read(conn, lobj_fd, buf, len - nread);
            buf[nbytes] = '\0';
            fprintf(stderr,">>> %s", buf);
            nread += nbytes;
           }
           fprintf(stderr,"\n");
           lo_close(conn, lobj_fd);
       }

       void overwrite(PGconn *conn, Oid lobjId, int start, int len)
       {
           int lobj_fd;
           char* buf;
           int nbytes;
           int nwritten;
           int i;

           lobj_fd = lo_open(conn, lobjId, INV_READ);
           if (lobj_fd < 0) {
            fprintf(stderr,"can't open large object %d",
                 lobjId);
           }

           lo_lseek(conn, lobj_fd, start, SEEK_SET);

           buf = malloc(len+1);

           for (i=0;i 0) {
            nbytes = lo_write(conn, lobj_fd, buf + nwritten, len - nwritten);
            nwritten += nbytes;
           }
           fprintf(stderr,"\n");
           lo_close(conn, lobj_fd);
       }

       /*
        * exportFile -
        *    export large object "lobjOid" to file "out_filename"
        *
        */
       void exportFile(PGconn *conn, Oid lobjId, char *filename)
       {
           int lobj_fd;
           char buf[BUFSIZE];
           int nbytes, tmp;
           int fd;

           /*
            * create an inversion "object"
            */
           lobj_fd = lo_open(conn, lobjId, INV_READ);
           if (lobj_fd < 0) {
            fprintf(stderr,"can't open large object %d",
                 lobjId);
           }

           /*
            * open the file to be written to
            */
           fd = open(filename, O_CREAT|O_WRONLY, 0666);
           if (fd < 0)  {   /* error */
            fprintf(stderr, "can't open unix file\"%s\"",
                 filename);
           }

           /*
            * read in from the Unix file and write to the inversion file
            */
           while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0) {
            tmp = write(fd, buf, nbytes);
               if (tmp < nbytes) {
                fprintf(stderr,"error while writing \"%s\"",
                     filename);

            }
           }

           (void) lo_close(conn, lobj_fd);
           (void) close(fd);

           return;
       }

       void
       exit_nicely(PGconn* conn)
       {
         PQfinish(conn);
         exit(1);
       }

       int
       main(int argc, char **argv)
       {
           char *in_filename, *out_filename;
           char *database;
           Oid lobjOid;
           PGconn *conn;
           PGresult *res;

           if (argc != 4) {
            fprintf(stderr, "Usage: %s database_name in_filename out_filename\n",
                 argv[0]);
            exit(1);
           }

           database = argv[1];
           in_filename = argv[2];
           out_filename = argv[3];

           /*
            * set up the connection
            */
           conn = PQsetdb(NULL, NULL, NULL, NULL, database);

           /* check to see that the backend connection was successfully made */
           if (PQstatus(conn) == CONNECTION_BAD) {
            fprintf(stderr,"Connection to database '%s' failed.\n", database);
            fprintf(stderr,"%s",PQerrorMessage(conn));
            exit_nicely(conn);
           }

           res = PQexec(conn, "begin work;");
           PQclear(res);
           printf("importing file \"%s\" ...\n", in_filename);
       /*  lobjOid = importFile(conn, in_filename); */
           lobjOid = lo_import(conn, in_filename);
       /*
           printf("\tas large object %d.\n", lobjOid);

           printf("picking out bytes 1000-2000 of the large object\n");
           pickout(conn, lobjOid, 1000, 1000);

           printf("overwriting bytes 1000-2000 of the large object with X's\n");
           overwrite(conn, lobjOid, 1000, 1000);
       */

           printf("exporting large object to file \"%s\" ...\n", out_filename);
       /*    exportFile(conn, lobjOid, out_filename); */
           lo_export(conn, lobjOid,out_filename);

           res = PQexec(conn, "commit;");
           PQclear(res);
           PQfinish(conn);
           exit(0);
       }

labs Home Page Subroutines Index LastKnownRequestProcessed