Skip to content

Commit 0763a56

Browse files
committed
Add lo_truncate() to backend and libpq for large object truncation.
Kris Jurka
1 parent 90d7652 commit 0763a56

File tree

12 files changed

+372
-26
lines changed

12 files changed

+372
-26
lines changed

doc/src/sgml/lobj.sgml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/lobj.sgml,v 1.44 2007/02/01 19:10:24 momjian Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/lobj.sgml,v 1.45 2007/03/03 19:52:45 momjian Exp $ -->
22

33
<chapter id="largeObjects">
44
<title id="largeObjects-title">Large Objects</title>
@@ -301,6 +301,37 @@ int lo_tell(PGconn *conn, int fd);
301301
</para>
302302
</sect2>
303303

304+
<sect2>
305+
<title>Truncating a Large Object</title>
306+
307+
<para>
308+
To truncate a large object to a given length, call
309+
<synopsis>
310+
int lo_truncate(PGcon *conn, int fd, size_t len);
311+
</synopsis>
312+
<indexterm><primary>lo_truncate</></> truncates the large object
313+
descriptor <parameter>fd</> to length <parameter>len</>. The
314+
<parameter>fd</parameter> argument must have been returned by a
315+
previous <function>lo_open</function>. If <parameter>len</> is
316+
greater than the current large object length, the large object
317+
is extended with null bytes ('\0').
318+
</para>
319+
320+
<para>
321+
The file offset is not changed.
322+
</para>
323+
324+
<para>
325+
On success <function>lo_truncate</function> returns
326+
zero. On error, the return value is negative.
327+
</para>
328+
329+
<para>
330+
<function>lo_truncate</> is new as of <productname>PostgreSQL</productname>
331+
8.3; if this function is run against an older server version, it will
332+
fail and return a negative value.
333+
</para>
334+
304335
<sect2>
305336
<title>Closing a Large Object Descriptor</title>
306337

src/backend/libpq/be-fsstubs.c

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/libpq/be-fsstubs.c,v 1.85 2007/02/27 23:48:07 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/libpq/be-fsstubs.c,v 1.86 2007/03/03 19:52:46 momjian Exp $
1212
*
1313
* NOTES
1414
* This should be moved to a more appropriate place. It is here
@@ -120,12 +120,10 @@ lo_close(PG_FUNCTION_ARGS)
120120
int32 fd = PG_GETARG_INT32(0);
121121

122122
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
123-
{
124123
ereport(ERROR,
125124
(errcode(ERRCODE_UNDEFINED_OBJECT),
126125
errmsg("invalid large-object descriptor: %d", fd)));
127-
PG_RETURN_INT32(-1);
128-
}
126+
129127
#if FSDB
130128
elog(DEBUG4, "lo_close(%d)", fd);
131129
#endif
@@ -152,12 +150,9 @@ lo_read(int fd, char *buf, int len)
152150
int status;
153151

154152
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
155-
{
156153
ereport(ERROR,
157154
(errcode(ERRCODE_UNDEFINED_OBJECT),
158155
errmsg("invalid large-object descriptor: %d", fd)));
159-
return -1;
160-
}
161156

162157
status = inv_read(cookies[fd], buf, len);
163158

@@ -170,12 +165,9 @@ lo_write(int fd, const char *buf, int len)
170165
int status;
171166

172167
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
173-
{
174168
ereport(ERROR,
175169
(errcode(ERRCODE_UNDEFINED_OBJECT),
176170
errmsg("invalid large-object descriptor: %d", fd)));
177-
return -1;
178-
}
179171

180172
if ((cookies[fd]->flags & IFS_WRLOCK) == 0)
181173
ereport(ERROR,
@@ -198,12 +190,9 @@ lo_lseek(PG_FUNCTION_ARGS)
198190
int status;
199191

200192
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
201-
{
202193
ereport(ERROR,
203194
(errcode(ERRCODE_UNDEFINED_OBJECT),
204195
errmsg("invalid large-object descriptor: %d", fd)));
205-
PG_RETURN_INT32(-1);
206-
}
207196

208197
status = inv_seek(cookies[fd], offset, whence);
209198

@@ -248,12 +237,9 @@ lo_tell(PG_FUNCTION_ARGS)
248237
int32 fd = PG_GETARG_INT32(0);
249238

250239
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
251-
{
252240
ereport(ERROR,
253241
(errcode(ERRCODE_UNDEFINED_OBJECT),
254242
errmsg("invalid large-object descriptor: %d", fd)));
255-
PG_RETURN_INT32(-1);
256-
}
257243

258244
PG_RETURN_INT32(inv_tell(cookies[fd]));
259245
}
@@ -467,6 +453,26 @@ lo_export(PG_FUNCTION_ARGS)
467453
PG_RETURN_INT32(1);
468454
}
469455

456+
/*
457+
* lo_truncate -
458+
* truncate a large object to a specified length
459+
*/
460+
Datum
461+
lo_truncate(PG_FUNCTION_ARGS)
462+
{
463+
int32 fd = PG_GETARG_INT32(0);
464+
int32 len = PG_GETARG_INT32(1);
465+
466+
if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
467+
ereport(ERROR,
468+
(errcode(ERRCODE_UNDEFINED_OBJECT),
469+
errmsg("invalid large-object descriptor: %d", fd)));
470+
471+
inv_truncate(cookies[fd], len);
472+
473+
PG_RETURN_INT32(0);
474+
}
475+
470476
/*
471477
* AtEOXact_LargeObject -
472478
* prepares large objects for transaction commit

src/backend/storage/large_object/inv_api.c

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*
1818
*
1919
* IDENTIFICATION
20-
* $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.122 2007/02/27 23:48:07 tgl Exp $
20+
* $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.123 2007/03/03 19:52:46 momjian Exp $
2121
*
2222
*-------------------------------------------------------------------------
2323
*/
@@ -681,3 +681,166 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
681681

682682
return nwritten;
683683
}
684+
685+
void
686+
inv_truncate(LargeObjectDesc *obj_desc, int len)
687+
{
688+
int32 pageno = (int32) (len / LOBLKSIZE);
689+
int off;
690+
ScanKeyData skey[2];
691+
IndexScanDesc sd;
692+
HeapTuple oldtuple;
693+
Form_pg_largeobject olddata;
694+
struct
695+
{
696+
bytea hdr;
697+
char data[LOBLKSIZE];
698+
} workbuf;
699+
char *workb = VARDATA(&workbuf.hdr);
700+
HeapTuple newtup;
701+
Datum values[Natts_pg_largeobject];
702+
char nulls[Natts_pg_largeobject];
703+
char replace[Natts_pg_largeobject];
704+
CatalogIndexState indstate;
705+
706+
Assert(PointerIsValid(obj_desc));
707+
708+
/* enforce writability because snapshot is probably wrong otherwise */
709+
if ((obj_desc->flags & IFS_WRLOCK) == 0)
710+
ereport(ERROR,
711+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
712+
errmsg("large object %u was not opened for writing",
713+
obj_desc->id)));
714+
715+
open_lo_relation();
716+
717+
indstate = CatalogOpenIndexes(lo_heap_r);
718+
719+
ScanKeyInit(&skey[0],
720+
Anum_pg_largeobject_loid,
721+
BTEqualStrategyNumber, F_OIDEQ,
722+
ObjectIdGetDatum(obj_desc->id));
723+
724+
ScanKeyInit(&skey[1],
725+
Anum_pg_largeobject_pageno,
726+
BTGreaterEqualStrategyNumber, F_INT4GE,
727+
Int32GetDatum(pageno));
728+
729+
sd = index_beginscan(lo_heap_r, lo_index_r,
730+
obj_desc->snapshot, 2, skey);
731+
732+
/*
733+
* If possible, get the page the truncation point is in.
734+
* The truncation point may be beyond the end of the LO or
735+
* in a hole.
736+
*/
737+
olddata = NULL;
738+
if ((oldtuple = index_getnext(sd, ForwardScanDirection)) != NULL)
739+
{
740+
olddata = (Form_pg_largeobject) GETSTRUCT(oldtuple);
741+
Assert(olddata->pageno >= pageno);
742+
}
743+
744+
/*
745+
* If we found the page of the truncation point we need to
746+
* truncate the data in it. Otherwise if we're in a hole,
747+
* we need to create a page to mark the end of data.
748+
*/
749+
if (olddata != NULL && olddata->pageno == pageno)
750+
{
751+
/* First, load old data into workbuf */
752+
bytea *datafield = &(olddata->data);
753+
bool pfreeit = false;
754+
int pagelen;
755+
756+
if (VARATT_IS_EXTENDED(datafield))
757+
{
758+
datafield = (bytea *)
759+
heap_tuple_untoast_attr((varattrib *) datafield);
760+
pfreeit = true;
761+
}
762+
pagelen = getbytealen(datafield);
763+
Assert(pagelen <= LOBLKSIZE);
764+
memcpy(workb, VARDATA(datafield), pagelen);
765+
if (pfreeit)
766+
pfree(datafield);
767+
768+
/*
769+
* Fill any hole
770+
*/
771+
off = len % LOBLKSIZE;
772+
if (off > pagelen)
773+
MemSet(workb + pagelen, 0, off - pagelen);
774+
775+
/* compute length of new page */
776+
SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ);
777+
778+
/*
779+
* Form and insert updated tuple
780+
*/
781+
memset(values, 0, sizeof(values));
782+
memset(nulls, ' ', sizeof(nulls));
783+
memset(replace, ' ', sizeof(replace));
784+
values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
785+
replace[Anum_pg_largeobject_data - 1] = 'r';
786+
newtup = heap_modifytuple(oldtuple, RelationGetDescr(lo_heap_r),
787+
values, nulls, replace);
788+
simple_heap_update(lo_heap_r, &newtup->t_self, newtup);
789+
CatalogIndexInsert(indstate, newtup);
790+
heap_freetuple(newtup);
791+
}
792+
else
793+
{
794+
/*
795+
* If the first page we found was after the truncation
796+
* point, we're in a hole that we'll fill, but we need to
797+
* delete the later page.
798+
*/
799+
if (olddata != NULL && olddata->pageno > pageno)
800+
simple_heap_delete(lo_heap_r, &oldtuple->t_self);
801+
802+
/*
803+
* Write a brand new page.
804+
*
805+
* Fill the hole up to the truncation point
806+
*/
807+
off = len % LOBLKSIZE;
808+
if (off > 0)
809+
MemSet(workb, 0, off);
810+
811+
/* compute length of new page */
812+
SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ);
813+
814+
/*
815+
* Form and insert new tuple
816+
*/
817+
memset(values, 0, sizeof(values));
818+
memset(nulls, ' ', sizeof(nulls));
819+
values[Anum_pg_largeobject_loid - 1] = ObjectIdGetDatum(obj_desc->id);
820+
values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno);
821+
values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
822+
newtup = heap_formtuple(lo_heap_r->rd_att, values, nulls);
823+
simple_heap_insert(lo_heap_r, newtup);
824+
CatalogIndexInsert(indstate, newtup);
825+
heap_freetuple(newtup);
826+
}
827+
828+
/*
829+
* Delete any pages after the truncation point
830+
*/
831+
while ((oldtuple = index_getnext(sd, ForwardScanDirection)) != NULL)
832+
{
833+
simple_heap_delete(lo_heap_r, &oldtuple->t_self);
834+
}
835+
836+
index_endscan(sd);
837+
838+
CatalogCloseIndexes(indstate);
839+
840+
/*
841+
* Advance command counter so that tuple updates will be seen by later
842+
* large-object operations in this transaction.
843+
*/
844+
CommandCounterIncrement();
845+
}
846+

src/include/catalog/pg_proc.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.446 2007/02/20 10:00:25 petere Exp $
10+
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.447 2007/03/03 19:52:46 momjian Exp $
1111
*
1212
* NOTES
1313
* 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
12331233
DESCR("large object create");
12341234
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_ ));
12351235
DESCR("large object position");
1236+
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_ ));
1237+
DESCR("truncate large object");
12361238

12371239
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_ ));
12381240
DESCR("point on line?");

src/include/libpq/be-fsstubs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/libpq/be-fsstubs.h,v 1.28 2007/01/05 22:19:55 momjian Exp $
10+
* $PostgreSQL: pgsql/src/include/libpq/be-fsstubs.h,v 1.29 2007/03/03 19:52:46 momjian Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -34,6 +34,7 @@ extern Datum lowrite(PG_FUNCTION_ARGS);
3434
extern Datum lo_lseek(PG_FUNCTION_ARGS);
3535
extern Datum lo_tell(PG_FUNCTION_ARGS);
3636
extern Datum lo_unlink(PG_FUNCTION_ARGS);
37+
extern Datum lo_truncate(PG_FUNCTION_ARGS);
3738

3839
/*
3940
* These are not fmgr-callable, but are available to C code.

src/include/storage/large_object.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
99
* Portions Copyright (c) 1994, Regents of the University of California
1010
*
11-
* $PostgreSQL: pgsql/src/include/storage/large_object.h,v 1.36 2007/01/05 22:19:58 momjian Exp $
11+
* $PostgreSQL: pgsql/src/include/storage/large_object.h,v 1.37 2007/03/03 19:52:46 momjian Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -78,5 +78,6 @@ extern int inv_seek(LargeObjectDesc *obj_desc, int offset, int whence);
7878
extern int inv_tell(LargeObjectDesc *obj_desc);
7979
extern int inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes);
8080
extern int inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes);
81+
extern void inv_truncate(LargeObjectDesc *obj_desc, int len);
8182

8283
#endif /* LARGE_OBJECT_H */

src/interfaces/libpq/exports.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.14 2006/08/18 19:52:39 tgl Exp $
1+
# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.15 2007/03/03 19:52:46 momjian Exp $
22
# Functions to be exported by libpq DLLs
33
PQconnectdb 1
44
PQsetdbLogin 2
@@ -136,3 +136,4 @@ PQdescribePrepared 133
136136
PQdescribePortal 134
137137
PQsendDescribePrepared 135
138138
PQsendDescribePortal 136
139+
lo_truncate 137

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy