Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)