a significant difference between
these modes and <symbol>INV_READ</symbol> alone: with <symbol>INV_READ</symbol>
you cannot write on the descriptor, and the data read from it will
reflect the contents of the large object at the time of the transaction
snapshot that was active when <function>lo_open</function> was executed,
regardless of later writes by this or other transactions. Reading
from a descriptor opened with <symbol>INV_WRITE</symbol> returns
data that reflects all writes of other committed transactions as well
as writes of the current transaction. This is similar to the behavior
of <literal>REPEATABLE READ</literal> versus <literal>READ COMMITTED</literal> transaction
modes for ordinary SQL <command>SELECT</command> commands.
</para>
<para>
<function>lo_open</function> will fail if <literal>SELECT</literal>
privilege is not available for the large object, or
if <symbol>INV_WRITE</symbol> is specified and <literal>UPDATE</literal>
privilege is not available.
(Prior to <productname>PostgreSQL</productname> 11, these privilege
checks were instead performed at the first actual read or write call
using the descriptor.)
These privilege checks can be disabled with the
<xref linkend="guc-lo-compat-privileges"/> run-time parameter.
</para>
<para>
An example:
<programlisting>
inv_fd = lo_open(conn, inv_oid, INV_READ|INV_WRITE);
</programlisting>
</para>
</sect2>
<sect2 id="lo-write">
<title>Writing Data to a Large Object</title>
<para>
<indexterm><primary>lo_write</primary></indexterm>
The function
<synopsis>
int lo_write(PGconn *conn, int fd, const char *buf, size_t len);
</synopsis>
writes <parameter>len</parameter> bytes from <parameter>buf</parameter>
(which must be of size <parameter>len</parameter>) to large object
descriptor <parameter>fd</parameter>. The <parameter>fd</parameter> argument must
have been returned by a previous <function>lo_open</function>. The
number of bytes actually written is returned (in the current
implementation, this will always equal <parameter>len</parameter> unless
there is an error). In the event of an error, the return value is -1.
</para>
<para>
Although the <parameter>len</parameter> parameter is declared as
<type>size_t</type>, this function will reject length values larger than
<literal>INT_MAX</literal>. In practice, it's best to transfer data in chunks
of at most a few megabytes anyway.
</para>
</sect2>
<sect2 id="lo-read">
<title>Reading Data from a Large Object</title>
<para>
<indexterm><primary>lo_read</primary></indexterm>
The function
<synopsis>
int lo_read(PGconn *conn, int fd, char *buf, size_t len);
</synopsis>
reads up to <parameter>len</parameter> bytes from large object descriptor
<parameter>fd</parameter> into <parameter>buf</parameter> (which must be
of size <parameter>len</parameter>). The <parameter>fd</parameter>
argument must have been returned by a previous
<function>lo_open</function>. The number of bytes actually read is
returned; this will be less than <parameter>len</parameter> if the end of
the large object is reached first. In the event of an error, the return
value is -1.
</para>
<para>
Although the <parameter>len</parameter> parameter is declared as
<type>size_t</type>, this function will reject length values larger than
<literal>INT_MAX</literal>. In practice, it's best to transfer data in chunks
of at most a few megabytes anyway.
</para>
</sect2>
<sect2 id="lo-seek">
<title>Seeking in a Large Object</title>
<para>
<indexterm><primary>lo_lseek</primary></indexterm>
To change the current read or write location associated with a
large object descriptor, call
<synopsis>
int lo_lseek(PGconn *conn, int fd, int offset, int whence);
</synopsis>
This function moves the
current