mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
Add lo_truncate() to backend and libpq for large object truncation.
Kris Jurka
This commit is contained in:
parent
90d76525c5
commit
0763a56501
@ -1,4 +1,4 @@
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/lobj.sgml,v 1.44 2007/02/01 19:10:24 momjian Exp $ -->
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/lobj.sgml,v 1.45 2007/03/03 19:52:45 momjian Exp $ -->
|
||||
|
||||
<chapter id="largeObjects">
|
||||
<title id="largeObjects-title">Large Objects</title>
|
||||
@ -301,6 +301,37 @@ int lo_tell(PGconn *conn, int fd);
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Truncating a Large Object</title>
|
||||
|
||||
<para>
|
||||
To truncate a large object to a given length, call
|
||||
<synopsis>
|
||||
int lo_truncate(PGcon *conn, int fd, size_t len);
|
||||
</synopsis>
|
||||
<indexterm><primary>lo_truncate</></> truncates the large object
|
||||
descriptor <parameter>fd</> to length <parameter>len</>. The
|
||||
<parameter>fd</parameter> argument must have been returned by a
|
||||
previous <function>lo_open</function>. If <parameter>len</> is
|
||||
greater than the current large object length, the large object
|
||||
is extended with null bytes ('\0').
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The file offset is not changed.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
On success <function>lo_truncate</function> returns
|
||||
zero. On error, the return value is negative.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<function>lo_truncate</> is new as of <productname>PostgreSQL</productname>
|
||||
8.3; if this function is run against an older server version, it will
|
||||
fail and return a negative value.
|
||||
</para>
|
||||
|
||||
<sect2>
|
||||
<title>Closing a Large Object Descriptor</title>
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/libpq/be-fsstubs.c,v 1.85 2007/02/27 23:48:07 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/libpq/be-fsstubs.c,v 1.86 2007/03/03 19:52:46 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* This should be moved to a more appropriate place. It is here
|
||||
@ -120,12 +120,10 @@ lo_close(PG_FUNCTION_ARGS)
|
||||
int32 fd = PG_GETARG_INT32(0);
|
||||
|
||||
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("invalid large-object descriptor: %d", fd)));
|
||||
PG_RETURN_INT32(-1);
|
||||
}
|
||||
|
||||
#if FSDB
|
||||
elog(DEBUG4, "lo_close(%d)", fd);
|
||||
#endif
|
||||
@ -152,12 +150,9 @@ lo_read(int fd, char *buf, int len)
|
||||
int status;
|
||||
|
||||
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("invalid large-object descriptor: %d", fd)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = inv_read(cookies[fd], buf, len);
|
||||
|
||||
@ -170,12 +165,9 @@ lo_write(int fd, const char *buf, int len)
|
||||
int status;
|
||||
|
||||
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("invalid large-object descriptor: %d", fd)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((cookies[fd]->flags & IFS_WRLOCK) == 0)
|
||||
ereport(ERROR,
|
||||
@ -198,12 +190,9 @@ lo_lseek(PG_FUNCTION_ARGS)
|
||||
int status;
|
||||
|
||||
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("invalid large-object descriptor: %d", fd)));
|
||||
PG_RETURN_INT32(-1);
|
||||
}
|
||||
|
||||
status = inv_seek(cookies[fd], offset, whence);
|
||||
|
||||
@ -248,12 +237,9 @@ lo_tell(PG_FUNCTION_ARGS)
|
||||
int32 fd = PG_GETARG_INT32(0);
|
||||
|
||||
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("invalid large-object descriptor: %d", fd)));
|
||||
PG_RETURN_INT32(-1);
|
||||
}
|
||||
|
||||
PG_RETURN_INT32(inv_tell(cookies[fd]));
|
||||
}
|
||||
@ -467,6 +453,26 @@ lo_export(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_INT32(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* lo_truncate -
|
||||
* truncate a large object to a specified length
|
||||
*/
|
||||
Datum
|
||||
lo_truncate(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int32 fd = PG_GETARG_INT32(0);
|
||||
int32 len = PG_GETARG_INT32(1);
|
||||
|
||||
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("invalid large-object descriptor: %d", fd)));
|
||||
|
||||
inv_truncate(cookies[fd], len);
|
||||
|
||||
PG_RETURN_INT32(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* AtEOXact_LargeObject -
|
||||
* prepares large objects for transaction commit
|
||||
|
@ -17,7 +17,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.122 2007/02/27 23:48:07 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.123 2007/03/03 19:52:46 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -681,3 +681,166 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
|
||||
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
void
|
||||
inv_truncate(LargeObjectDesc *obj_desc, int len)
|
||||
{
|
||||
int32 pageno = (int32) (len / LOBLKSIZE);
|
||||
int off;
|
||||
ScanKeyData skey[2];
|
||||
IndexScanDesc sd;
|
||||
HeapTuple oldtuple;
|
||||
Form_pg_largeobject olddata;
|
||||
struct
|
||||
{
|
||||
bytea hdr;
|
||||
char data[LOBLKSIZE];
|
||||
} workbuf;
|
||||
char *workb = VARDATA(&workbuf.hdr);
|
||||
HeapTuple newtup;
|
||||
Datum values[Natts_pg_largeobject];
|
||||
char nulls[Natts_pg_largeobject];
|
||||
char replace[Natts_pg_largeobject];
|
||||
CatalogIndexState indstate;
|
||||
|
||||
Assert(PointerIsValid(obj_desc));
|
||||
|
||||
/* enforce writability because snapshot is probably wrong otherwise */
|
||||
if ((obj_desc->flags & IFS_WRLOCK) == 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("large object %u was not opened for writing",
|
||||
obj_desc->id)));
|
||||
|
||||
open_lo_relation();
|
||||
|
||||
indstate = CatalogOpenIndexes(lo_heap_r);
|
||||
|
||||
ScanKeyInit(&skey[0],
|
||||
Anum_pg_largeobject_loid,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(obj_desc->id));
|
||||
|
||||
ScanKeyInit(&skey[1],
|
||||
Anum_pg_largeobject_pageno,
|
||||
BTGreaterEqualStrategyNumber, F_INT4GE,
|
||||
Int32GetDatum(pageno));
|
||||
|
||||
sd = index_beginscan(lo_heap_r, lo_index_r,
|
||||
obj_desc->snapshot, 2, skey);
|
||||
|
||||
/*
|
||||
* If possible, get the page the truncation point is in.
|
||||
* The truncation point may be beyond the end of the LO or
|
||||
* in a hole.
|
||||
*/
|
||||
olddata = NULL;
|
||||
if ((oldtuple = index_getnext(sd, ForwardScanDirection)) != NULL)
|
||||
{
|
||||
olddata = (Form_pg_largeobject) GETSTRUCT(oldtuple);
|
||||
Assert(olddata->pageno >= pageno);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we found the page of the truncation point we need to
|
||||
* truncate the data in it. Otherwise if we're in a hole,
|
||||
* we need to create a page to mark the end of data.
|
||||
*/
|
||||
if (olddata != NULL && olddata->pageno == pageno)
|
||||
{
|
||||
/* First, load old data into workbuf */
|
||||
bytea *datafield = &(olddata->data);
|
||||
bool pfreeit = false;
|
||||
int pagelen;
|
||||
|
||||
if (VARATT_IS_EXTENDED(datafield))
|
||||
{
|
||||
datafield = (bytea *)
|
||||
heap_tuple_untoast_attr((varattrib *) datafield);
|
||||
pfreeit = true;
|
||||
}
|
||||
pagelen = getbytealen(datafield);
|
||||
Assert(pagelen <= LOBLKSIZE);
|
||||
memcpy(workb, VARDATA(datafield), pagelen);
|
||||
if (pfreeit)
|
||||
pfree(datafield);
|
||||
|
||||
/*
|
||||
* Fill any hole
|
||||
*/
|
||||
off = len % LOBLKSIZE;
|
||||
if (off > pagelen)
|
||||
MemSet(workb + pagelen, 0, off - pagelen);
|
||||
|
||||
/* compute length of new page */
|
||||
SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ);
|
||||
|
||||
/*
|
||||
* Form and insert updated tuple
|
||||
*/
|
||||
memset(values, 0, sizeof(values));
|
||||
memset(nulls, ' ', sizeof(nulls));
|
||||
memset(replace, ' ', sizeof(replace));
|
||||
values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
|
||||
replace[Anum_pg_largeobject_data - 1] = 'r';
|
||||
newtup = heap_modifytuple(oldtuple, RelationGetDescr(lo_heap_r),
|
||||
values, nulls, replace);
|
||||
simple_heap_update(lo_heap_r, &newtup->t_self, newtup);
|
||||
CatalogIndexInsert(indstate, newtup);
|
||||
heap_freetuple(newtup);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* If the first page we found was after the truncation
|
||||
* point, we're in a hole that we'll fill, but we need to
|
||||
* delete the later page.
|
||||
*/
|
||||
if (olddata != NULL && olddata->pageno > pageno)
|
||||
simple_heap_delete(lo_heap_r, &oldtuple->t_self);
|
||||
|
||||
/*
|
||||
* Write a brand new page.
|
||||
*
|
||||
* Fill the hole up to the truncation point
|
||||
*/
|
||||
off = len % LOBLKSIZE;
|
||||
if (off > 0)
|
||||
MemSet(workb, 0, off);
|
||||
|
||||
/* compute length of new page */
|
||||
SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ);
|
||||
|
||||
/*
|
||||
* Form and insert new tuple
|
||||
*/
|
||||
memset(values, 0, sizeof(values));
|
||||
memset(nulls, ' ', sizeof(nulls));
|
||||
values[Anum_pg_largeobject_loid - 1] = ObjectIdGetDatum(obj_desc->id);
|
||||
values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno);
|
||||
values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
|
||||
newtup = heap_formtuple(lo_heap_r->rd_att, values, nulls);
|
||||
simple_heap_insert(lo_heap_r, newtup);
|
||||
CatalogIndexInsert(indstate, newtup);
|
||||
heap_freetuple(newtup);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete any pages after the truncation point
|
||||
*/
|
||||
while ((oldtuple = index_getnext(sd, ForwardScanDirection)) != NULL)
|
||||
{
|
||||
simple_heap_delete(lo_heap_r, &oldtuple->t_self);
|
||||
}
|
||||
|
||||
index_endscan(sd);
|
||||
|
||||
CatalogCloseIndexes(indstate);
|
||||
|
||||
/*
|
||||
* Advance command counter so that tuple updates will be seen by later
|
||||
* large-object operations in this transaction.
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.446 2007/02/20 10:00:25 petere Exp $
|
||||
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.447 2007/03/03 19:52:46 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* The script catalog/genbki.sh reads this file and generates .bki
|
||||
@ -1233,6 +1233,8 @@ DATA(insert OID = 715 ( lo_create PGNSP PGUID 12 1 0 f f t f v 1 26 "26" _n
|
||||
DESCR("large object create");
|
||||
DATA(insert OID = 958 ( lo_tell PGNSP PGUID 12 1 0 f f t f v 1 23 "23" _null_ _null_ _null_ lo_tell - _null_ ));
|
||||
DESCR("large object position");
|
||||
DATA(insert OID = 1004 ( lo_truncate PGNSP PGUID 12 1 0 f f t f v 2 23 "23 23" _null_ _null_ _null_ lo_truncate - _null_ ));
|
||||
DESCR("truncate large object");
|
||||
|
||||
DATA(insert OID = 959 ( on_pl PGNSP PGUID 12 1 0 f f t f i 2 16 "600 628" _null_ _null_ _null_ on_pl - _null_ ));
|
||||
DESCR("point on line?");
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/libpq/be-fsstubs.h,v 1.28 2007/01/05 22:19:55 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/libpq/be-fsstubs.h,v 1.29 2007/03/03 19:52:46 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -34,6 +34,7 @@ extern Datum lowrite(PG_FUNCTION_ARGS);
|
||||
extern Datum lo_lseek(PG_FUNCTION_ARGS);
|
||||
extern Datum lo_tell(PG_FUNCTION_ARGS);
|
||||
extern Datum lo_unlink(PG_FUNCTION_ARGS);
|
||||
extern Datum lo_truncate(PG_FUNCTION_ARGS);
|
||||
|
||||
/*
|
||||
* These are not fmgr-callable, but are available to C code.
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/storage/large_object.h,v 1.36 2007/01/05 22:19:58 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/storage/large_object.h,v 1.37 2007/03/03 19:52:46 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -78,5 +78,6 @@ extern int inv_seek(LargeObjectDesc *obj_desc, int offset, int whence);
|
||||
extern int inv_tell(LargeObjectDesc *obj_desc);
|
||||
extern int inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes);
|
||||
extern int inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes);
|
||||
extern void inv_truncate(LargeObjectDesc *obj_desc, int len);
|
||||
|
||||
#endif /* LARGE_OBJECT_H */
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.14 2006/08/18 19:52:39 tgl Exp $
|
||||
# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.15 2007/03/03 19:52:46 momjian Exp $
|
||||
# Functions to be exported by libpq DLLs
|
||||
PQconnectdb 1
|
||||
PQsetdbLogin 2
|
||||
@ -136,3 +136,4 @@ PQdescribePrepared 133
|
||||
PQdescribePortal 134
|
||||
PQsendDescribePrepared 135
|
||||
PQsendDescribePortal 136
|
||||
lo_truncate 137
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-lobj.c,v 1.61 2007/01/05 22:20:01 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-lobj.c,v 1.62 2007/03/03 19:52:46 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -122,6 +122,59 @@ lo_close(PGconn *conn, int fd)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* lo_truncate
|
||||
* truncates an existing large object to the given size
|
||||
*
|
||||
* returns 0 upon success
|
||||
* returns -1 upon failure
|
||||
*/
|
||||
int
|
||||
lo_truncate(PGconn *conn, int fd, size_t len)
|
||||
{
|
||||
PQArgBlock argv[2];
|
||||
PGresult *res;
|
||||
int retval;
|
||||
int result_len;
|
||||
|
||||
if (conn->lobjfuncs == NULL)
|
||||
{
|
||||
if (lo_initialize(conn) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Must check this on-the-fly because it's not there pre-8.3 */
|
||||
if (conn->lobjfuncs->fn_lo_truncate == 0)
|
||||
{
|
||||
printfPQExpBuffer(&conn->errorMessage,
|
||||
libpq_gettext("cannot determine OID of function lo_truncate\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
argv[0].isint = 1;
|
||||
argv[0].len = 4;
|
||||
argv[0].u.integer = fd;
|
||||
|
||||
argv[1].isint = 1;
|
||||
argv[1].len = 4;
|
||||
argv[1].u.integer = len;
|
||||
|
||||
res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate,
|
||||
&retval, &result_len, 1, argv, 2);
|
||||
|
||||
if (PQresultStatus(res) == PGRES_COMMAND_OK)
|
||||
{
|
||||
PQclear(res);
|
||||
return retval;
|
||||
}
|
||||
else
|
||||
{
|
||||
PQclear(res);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* lo_read
|
||||
* read len bytes of the large object into buf
|
||||
@ -621,6 +674,7 @@ lo_initialize(PGconn *conn)
|
||||
/*
|
||||
* Execute the query to get all the functions at once. In 7.3 and later
|
||||
* we need to be schema-safe. lo_create only exists in 8.1 and up.
|
||||
* lo_truncate only exists in 8.3 and up.
|
||||
*/
|
||||
if (conn->sversion >= 70300)
|
||||
query = "select proname, oid from pg_catalog.pg_proc "
|
||||
@ -632,6 +686,7 @@ lo_initialize(PGconn *conn)
|
||||
"'lo_unlink', "
|
||||
"'lo_lseek', "
|
||||
"'lo_tell', "
|
||||
"'lo_truncate', "
|
||||
"'loread', "
|
||||
"'lowrite') "
|
||||
"and pronamespace = (select oid from pg_catalog.pg_namespace "
|
||||
@ -684,6 +739,8 @@ lo_initialize(PGconn *conn)
|
||||
lobjfuncs->fn_lo_lseek = foid;
|
||||
else if (!strcmp(fname, "lo_tell"))
|
||||
lobjfuncs->fn_lo_tell = foid;
|
||||
else if (!strcmp(fname, "lo_truncate"))
|
||||
lobjfuncs->fn_lo_truncate = foid;
|
||||
else if (!strcmp(fname, "loread"))
|
||||
lobjfuncs->fn_lo_read = foid;
|
||||
else if (!strcmp(fname, "lowrite"))
|
||||
@ -694,7 +751,6 @@ lo_initialize(PGconn *conn)
|
||||
|
||||
/*
|
||||
* Finally check that we really got all large object interface functions
|
||||
* --- except lo_create, which may not exist.
|
||||
*/
|
||||
if (lobjfuncs->fn_lo_open == 0)
|
||||
{
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.135 2007/01/05 22:20:01 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.136 2007/03/03 19:52:46 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -489,6 +489,7 @@ extern int lo_lseek(PGconn *conn, int fd, int offset, int whence);
|
||||
extern Oid lo_creat(PGconn *conn, int mode);
|
||||
extern Oid lo_create(PGconn *conn, Oid lobjId);
|
||||
extern int lo_tell(PGconn *conn, int fd);
|
||||
extern int lo_truncate(PGconn *conn, int fd, size_t len);
|
||||
extern int lo_unlink(PGconn *conn, Oid lobjId);
|
||||
extern Oid lo_import(PGconn *conn, const char *filename);
|
||||
extern int lo_export(PGconn *conn, Oid lobjId, const char *filename);
|
||||
|
@ -12,7 +12,7 @@
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.118 2007/01/26 17:45:41 neilc Exp $
|
||||
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.119 2007/03/03 19:52:47 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -239,6 +239,7 @@ typedef struct pgLobjfuncs
|
||||
Oid fn_lo_unlink; /* OID of backend function lo_unlink */
|
||||
Oid fn_lo_lseek; /* OID of backend function lo_lseek */
|
||||
Oid fn_lo_tell; /* OID of backend function lo_tell */
|
||||
Oid fn_lo_truncate; /* OID of backend function lo_truncate */
|
||||
Oid fn_lo_read; /* OID of backend function LOread */
|
||||
Oid fn_lo_write; /* OID of backend function LOwrite */
|
||||
} PGlobjfuncs;
|
||||
|
@ -83,6 +83,25 @@ SELECT lo_close(fd) FROM lotest_stash_values;
|
||||
|
||||
END;
|
||||
|
||||
-- Test truncation.
|
||||
BEGIN;
|
||||
UPDATE lotest_stash_values SET fd=lo_open(loid, CAST((2 | 4) * 16^4 AS integer));
|
||||
|
||||
SELECT lo_truncate(fd, 10) FROM lotest_stash_values;
|
||||
SELECT loread(fd, 15) FROM lotest_stash_values;
|
||||
|
||||
SELECT lo_truncate(fd, 10000) FROM lotest_stash_values;
|
||||
SELECT loread(fd, 10) FROM lotest_stash_values;
|
||||
SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
|
||||
SELECT lo_tell(fd) FROM lotest_stash_values;
|
||||
|
||||
SELECT lo_truncate(fd, 5000) FROM lotest_stash_values;
|
||||
SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
|
||||
SELECT lo_tell(fd) FROM lotest_stash_values;
|
||||
|
||||
SELECT lo_close(fd) FROM lotest_stash_values;
|
||||
END;
|
||||
|
||||
-- lo_unlink(lobjId oid) returns integer
|
||||
-- return value appears to always be 1
|
||||
SELECT lo_unlink(loid) from lotest_stash_values;
|
||||
|
@ -115,6 +115,70 @@ SELECT lo_close(fd) FROM lotest_stash_values;
|
||||
0
|
||||
(1 row)
|
||||
|
||||
END;
|
||||
-- Test truncation.
|
||||
BEGIN;
|
||||
UPDATE lotest_stash_values SET fd=lo_open(loid, CAST((2 | 4) * 16^4 AS integer));
|
||||
SELECT lo_truncate(fd, 10) FROM lotest_stash_values;
|
||||
lo_truncate
|
||||
-------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT loread(fd, 15) FROM lotest_stash_values;
|
||||
loread
|
||||
---------------
|
||||
\012Whose woo
|
||||
(1 row)
|
||||
|
||||
SELECT lo_truncate(fd, 10000) FROM lotest_stash_values;
|
||||
lo_truncate
|
||||
-------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT loread(fd, 10) FROM lotest_stash_values;
|
||||
loread
|
||||
------------------------------------------
|
||||
\000\000\000\000\000\000\000\000\000\000
|
||||
(1 row)
|
||||
|
||||
SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
|
||||
lo_lseek
|
||||
----------
|
||||
10000
|
||||
(1 row)
|
||||
|
||||
SELECT lo_tell(fd) FROM lotest_stash_values;
|
||||
lo_tell
|
||||
---------
|
||||
10000
|
||||
(1 row)
|
||||
|
||||
SELECT lo_truncate(fd, 5000) FROM lotest_stash_values;
|
||||
lo_truncate
|
||||
-------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
|
||||
lo_lseek
|
||||
----------
|
||||
5000
|
||||
(1 row)
|
||||
|
||||
SELECT lo_tell(fd) FROM lotest_stash_values;
|
||||
lo_tell
|
||||
---------
|
||||
5000
|
||||
(1 row)
|
||||
|
||||
SELECT lo_close(fd) FROM lotest_stash_values;
|
||||
lo_close
|
||||
----------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
END;
|
||||
-- lo_unlink(lobjId oid) returns integer
|
||||
-- return value appears to always be 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user