PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
inv_api.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * inv_api.c
4  * routines for manipulating inversion fs large objects. This file
5  * contains the user-level large object application interface routines.
6  *
7  *
8  * Note: we access pg_largeobject.data using its C struct declaration.
9  * This is safe because it immediately follows pageno which is an int4 field,
10  * and therefore the data field will always be 4-byte aligned, even if it
11  * is in the short 1-byte-header format. We have to detoast it since it's
12  * quite likely to be in compressed or short format. We also need to check
13  * for NULLs, since initdb will mark loid and pageno but not data as NOT NULL.
14  *
15  * Note: many of these routines leak memory in CurrentMemoryContext, as indeed
16  * does most of the backend code. We expect that CurrentMemoryContext will
17  * be a short-lived context. Data that must persist across function calls
18  * is kept either in CacheMemoryContext (the Relation structs) or in the
19  * memory context given to inv_open (for LargeObjectDesc structs).
20  *
21  *
22  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
23  * Portions Copyright (c) 1994, Regents of the University of California
24  *
25  *
26  * IDENTIFICATION
27  * src/backend/storage/large_object/inv_api.c
28  *
29  *-------------------------------------------------------------------------
30  */
31 #include "postgres.h"
32 
33 #include <limits.h>
34 
35 #include "access/genam.h"
36 #include "access/heapam.h"
37 #include "access/sysattr.h"
38 #include "access/tuptoaster.h"
39 #include "access/xact.h"
40 #include "catalog/dependency.h"
41 #include "catalog/indexing.h"
42 #include "catalog/objectaccess.h"
43 #include "catalog/pg_largeobject.h"
45 #include "libpq/libpq-fs.h"
46 #include "miscadmin.h"
47 #include "storage/large_object.h"
48 #include "utils/fmgroids.h"
49 #include "utils/rel.h"
50 #include "utils/snapmgr.h"
51 #include "utils/tqual.h"
52 
53 
54 /*
55  * All accesses to pg_largeobject and its index make use of a single Relation
56  * reference, so that we only need to open pg_relation once per transaction.
57  * To avoid problems when the first such reference occurs inside a
58  * subtransaction, we execute a slightly klugy maneuver to assign ownership of
59  * the Relation reference to TopTransactionResourceOwner.
60  */
61 static Relation lo_heap_r = NULL;
62 static Relation lo_index_r = NULL;
63 
64 
65 /*
66  * Open pg_largeobject and its index, if not already done in current xact
67  */
68 static void
70 {
71  ResourceOwner currentOwner;
72 
73  if (lo_heap_r && lo_index_r)
74  return; /* already open in current xact */
75 
76  /* Arrange for the top xact to own these relation references */
77  currentOwner = CurrentResourceOwner;
79 
80  /* Use RowExclusiveLock since we might either read or write */
81  if (lo_heap_r == NULL)
83  if (lo_index_r == NULL)
85 
86  CurrentResourceOwner = currentOwner;
87 }
88 
89 /*
90  * Clean up at main transaction end
91  */
92 void
93 close_lo_relation(bool isCommit)
94 {
95  if (lo_heap_r || lo_index_r)
96  {
97  /*
98  * Only bother to close if committing; else abort cleanup will handle
99  * it
100  */
101  if (isCommit)
102  {
103  ResourceOwner currentOwner;
104 
105  currentOwner = CurrentResourceOwner;
107 
108  if (lo_index_r)
109  index_close(lo_index_r, NoLock);
110  if (lo_heap_r)
111  heap_close(lo_heap_r, NoLock);
112 
113  CurrentResourceOwner = currentOwner;
114  }
115  lo_heap_r = NULL;
116  lo_index_r = NULL;
117  }
118 }
119 
120 
121 /*
122  * Same as pg_largeobject.c's LargeObjectExists(), except snapshot to
123  * read with can be specified.
124  */
125 static bool
127 {
128  Relation pg_lo_meta;
129  ScanKeyData skey[1];
130  SysScanDesc sd;
131  HeapTuple tuple;
132  bool retval = false;
133 
134  ScanKeyInit(&skey[0],
136  BTEqualStrategyNumber, F_OIDEQ,
137  ObjectIdGetDatum(loid));
138 
141 
142  sd = systable_beginscan(pg_lo_meta,
144  snapshot, 1, skey);
145 
146  tuple = systable_getnext(sd);
147  if (HeapTupleIsValid(tuple))
148  retval = true;
149 
150  systable_endscan(sd);
151 
152  heap_close(pg_lo_meta, AccessShareLock);
153 
154  return retval;
155 }
156 
157 
158 /*
159  * Extract data field from a pg_largeobject tuple, detoasting if needed
160  * and verifying that the length is sane. Returns data pointer (a bytea *),
161  * data length, and an indication of whether to pfree the data pointer.
162  */
163 static void
165  bytea **pdatafield,
166  int *plen,
167  bool *pfreeit)
168 {
169  bytea *datafield;
170  int len;
171  bool freeit;
172 
173  datafield = &(tuple->data); /* see note at top of file */
174  freeit = false;
175  if (VARATT_IS_EXTENDED(datafield))
176  {
177  datafield = (bytea *)
178  heap_tuple_untoast_attr((struct varlena *) datafield);
179  freeit = true;
180  }
181  len = VARSIZE(datafield) - VARHDRSZ;
182  if (len < 0 || len > LOBLKSIZE)
183  ereport(ERROR,
184  (errcode(ERRCODE_DATA_CORRUPTED),
185  errmsg("pg_largeobject entry for OID %u, page %d has invalid data field size %d",
186  tuple->loid, tuple->pageno, len)));
187  *pdatafield = datafield;
188  *plen = len;
189  *pfreeit = freeit;
190 }
191 
192 
193 /*
194  * inv_create -- create a new large object
195  *
196  * Arguments:
197  * lobjId - OID to use for new large object, or InvalidOid to pick one
198  *
199  * Returns:
200  * OID of new object
201  *
202  * If lobjId is not InvalidOid, then an error occurs if the OID is already
203  * in use.
204  */
205 Oid
207 {
208  Oid lobjId_new;
209 
210  /*
211  * Create a new largeobject with empty data pages
212  */
213  lobjId_new = LargeObjectCreate(lobjId);
214 
215  /*
216  * dependency on the owner of largeobject
217  *
218  * The reason why we use LargeObjectRelationId instead of
219  * LargeObjectMetadataRelationId here is to provide backward compatibility
220  * to the applications which utilize a knowledge about internal layout of
221  * system catalogs. OID of pg_largeobject_metadata and loid of
222  * pg_largeobject are same value, so there are no actual differences here.
223  */
225  lobjId_new, GetUserId());
226 
227  /* Post creation hook for new large object */
229 
230  /*
231  * Advance command counter to make new tuple visible to later operations.
232  */
234 
235  return lobjId_new;
236 }
237 
238 /*
239  * inv_open -- access an existing large object.
240  *
241  * Returns:
242  * Large object descriptor, appropriately filled in. The descriptor
243  * and subsidiary data are allocated in the specified memory context,
244  * which must be suitably long-lived for the caller's purposes.
245  */
247 inv_open(Oid lobjId, int flags, MemoryContext mcxt)
248 {
249  LargeObjectDesc *retval;
250  Snapshot snapshot = NULL;
251  int descflags = 0;
252 
253  if (flags & INV_WRITE)
254  {
255  snapshot = NULL; /* instantaneous MVCC snapshot */
256  descflags = IFS_WRLOCK | IFS_RDLOCK;
257  }
258  else if (flags & INV_READ)
259  {
260  snapshot = GetActiveSnapshot();
261  descflags = IFS_RDLOCK;
262  }
263  else
264  ereport(ERROR,
265  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
266  errmsg("invalid flags for opening a large object: %d",
267  flags)));
268 
269  /* Can't use LargeObjectExists here because we need to specify snapshot */
270  if (!myLargeObjectExists(lobjId, snapshot))
271  ereport(ERROR,
272  (errcode(ERRCODE_UNDEFINED_OBJECT),
273  errmsg("large object %u does not exist", lobjId)));
274 
275  /*
276  * We must register the snapshot in TopTransaction's resowner, because it
277  * must stay alive until the LO is closed rather than until the current
278  * portal shuts down. Do this after checking that the LO exists, to avoid
279  * leaking the snapshot if an error is thrown.
280  */
281  if (snapshot)
282  snapshot = RegisterSnapshotOnOwner(snapshot,
284 
285  /* All set, create a descriptor */
286  retval = (LargeObjectDesc *) MemoryContextAlloc(mcxt,
287  sizeof(LargeObjectDesc));
288  retval->id = lobjId;
289  retval->subid = GetCurrentSubTransactionId();
290  retval->offset = 0;
291  retval->snapshot = snapshot;
292  retval->flags = descflags;
293 
294  return retval;
295 }
296 
297 /*
298  * Closes a large object descriptor previously made by inv_open(), and
299  * releases the long-term memory used by it.
300  */
301 void
303 {
304  Assert(PointerIsValid(obj_desc));
305 
308 
309  pfree(obj_desc);
310 }
311 
312 /*
313  * Destroys an existing large object (not to be confused with a descriptor!)
314  *
315  * returns -1 if failed
316  */
317 int
318 inv_drop(Oid lobjId)
319 {
320  ObjectAddress object;
321 
322  /*
323  * Delete any comments and dependencies on the large object
324  */
326  object.objectId = lobjId;
327  object.objectSubId = 0;
328  performDeletion(&object, DROP_CASCADE, 0);
329 
330  /*
331  * Advance command counter so that tuple removal will be seen by later
332  * large-object operations in this transaction.
333  */
335 
336  return 1;
337 }
338 
339 /*
340  * Determine size of a large object
341  *
342  * NOTE: LOs can contain gaps, just like Unix files. We actually return
343  * the offset of the last byte + 1.
344  */
345 static uint64
347 {
348  uint64 lastbyte = 0;
349  ScanKeyData skey[1];
350  SysScanDesc sd;
351  HeapTuple tuple;
352 
353  Assert(PointerIsValid(obj_desc));
354 
356 
357  ScanKeyInit(&skey[0],
359  BTEqualStrategyNumber, F_OIDEQ,
360  ObjectIdGetDatum(obj_desc->id));
361 
362  sd = systable_beginscan_ordered(lo_heap_r, lo_index_r,
363  obj_desc->snapshot, 1, skey);
364 
365  /*
366  * Because the pg_largeobject index is on both loid and pageno, but we
367  * constrain only loid, a backwards scan should visit all pages of the
368  * large object in reverse pageno order. So, it's sufficient to examine
369  * the first valid tuple (== last valid page).
370  */
372  if (HeapTupleIsValid(tuple))
373  {
374  Form_pg_largeobject data;
375  bytea *datafield;
376  int len;
377  bool pfreeit;
378 
379  if (HeapTupleHasNulls(tuple)) /* paranoia */
380  elog(ERROR, "null field found in pg_largeobject");
381  data = (Form_pg_largeobject) GETSTRUCT(tuple);
382  getdatafield(data, &datafield, &len, &pfreeit);
383  lastbyte = (uint64) data->pageno * LOBLKSIZE + len;
384  if (pfreeit)
385  pfree(datafield);
386  }
387 
389 
390  return lastbyte;
391 }
392 
393 int64
394 inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence)
395 {
396  int64 newoffset;
397 
398  Assert(PointerIsValid(obj_desc));
399 
400  /*
401  * Note: overflow in the additions is possible, but since we will reject
402  * negative results, we don't need any extra test for that.
403  */
404  switch (whence)
405  {
406  case SEEK_SET:
407  newoffset = offset;
408  break;
409  case SEEK_CUR:
410  newoffset = obj_desc->offset + offset;
411  break;
412  case SEEK_END:
413  newoffset = inv_getsize(obj_desc) + offset;
414  break;
415  default:
416  ereport(ERROR,
417  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
418  errmsg("invalid whence setting: %d", whence)));
419  newoffset = 0; /* keep compiler quiet */
420  break;
421  }
422 
423  /*
424  * use errmsg_internal here because we don't want to expose INT64_FORMAT
425  * in translatable strings; doing better is not worth the trouble
426  */
427  if (newoffset < 0 || newoffset > MAX_LARGE_OBJECT_SIZE)
428  ereport(ERROR,
429  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
430  errmsg_internal("invalid large object seek target: " INT64_FORMAT,
431  newoffset)));
432 
433  obj_desc->offset = newoffset;
434  return newoffset;
435 }
436 
437 int64
439 {
440  Assert(PointerIsValid(obj_desc));
441 
442  return obj_desc->offset;
443 }
444 
445 int
446 inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
447 {
448  int nread = 0;
449  int64 n;
450  int64 off;
451  int len;
452  int32 pageno = (int32) (obj_desc->offset / LOBLKSIZE);
453  uint64 pageoff;
454  ScanKeyData skey[2];
455  SysScanDesc sd;
456  HeapTuple tuple;
457 
458  Assert(PointerIsValid(obj_desc));
459  Assert(buf != NULL);
460 
461  if (nbytes <= 0)
462  return 0;
463 
465 
466  ScanKeyInit(&skey[0],
468  BTEqualStrategyNumber, F_OIDEQ,
469  ObjectIdGetDatum(obj_desc->id));
470 
471  ScanKeyInit(&skey[1],
474  Int32GetDatum(pageno));
475 
476  sd = systable_beginscan_ordered(lo_heap_r, lo_index_r,
477  obj_desc->snapshot, 2, skey);
478 
479  while ((tuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL)
480  {
481  Form_pg_largeobject data;
482  bytea *datafield;
483  bool pfreeit;
484 
485  if (HeapTupleHasNulls(tuple)) /* paranoia */
486  elog(ERROR, "null field found in pg_largeobject");
487  data = (Form_pg_largeobject) GETSTRUCT(tuple);
488 
489  /*
490  * We expect the indexscan will deliver pages in order. However,
491  * there may be missing pages if the LO contains unwritten "holes". We
492  * want missing sections to read out as zeroes.
493  */
494  pageoff = ((uint64) data->pageno) * LOBLKSIZE;
495  if (pageoff > obj_desc->offset)
496  {
497  n = pageoff - obj_desc->offset;
498  n = (n <= (nbytes - nread)) ? n : (nbytes - nread);
499  MemSet(buf + nread, 0, n);
500  nread += n;
501  obj_desc->offset += n;
502  }
503 
504  if (nread < nbytes)
505  {
506  Assert(obj_desc->offset >= pageoff);
507  off = (int) (obj_desc->offset - pageoff);
508  Assert(off >= 0 && off < LOBLKSIZE);
509 
510  getdatafield(data, &datafield, &len, &pfreeit);
511  if (len > off)
512  {
513  n = len - off;
514  n = (n <= (nbytes - nread)) ? n : (nbytes - nread);
515  memcpy(buf + nread, VARDATA(datafield) + off, n);
516  nread += n;
517  obj_desc->offset += n;
518  }
519  if (pfreeit)
520  pfree(datafield);
521  }
522 
523  if (nread >= nbytes)
524  break;
525  }
526 
528 
529  return nread;
530 }
531 
532 int
533 inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
534 {
535  int nwritten = 0;
536  int n;
537  int off;
538  int len;
539  int32 pageno = (int32) (obj_desc->offset / LOBLKSIZE);
540  ScanKeyData skey[2];
541  SysScanDesc sd;
542  HeapTuple oldtuple;
543  Form_pg_largeobject olddata;
544  bool neednextpage;
545  bytea *datafield;
546  bool pfreeit;
547  union
548  {
549  bytea hdr;
550  /* this is to make the union big enough for a LO data chunk: */
551  char data[LOBLKSIZE + VARHDRSZ];
552  /* ensure union is aligned well enough: */
553  int32 align_it;
554  } workbuf;
555  char *workb = VARDATA(&workbuf.hdr);
556  HeapTuple newtup;
558  bool nulls[Natts_pg_largeobject];
559  bool replace[Natts_pg_largeobject];
560  CatalogIndexState indstate;
561 
562  Assert(PointerIsValid(obj_desc));
563  Assert(buf != NULL);
564 
565  /* enforce writability because snapshot is probably wrong otherwise */
566  Assert(obj_desc->flags & IFS_WRLOCK);
567 
568  if (nbytes <= 0)
569  return 0;
570 
571  /* this addition can't overflow because nbytes is only int32 */
572  if ((nbytes + obj_desc->offset) > MAX_LARGE_OBJECT_SIZE)
573  ereport(ERROR,
574  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
575  errmsg("invalid large object write request size: %d",
576  nbytes)));
577 
579 
580  indstate = CatalogOpenIndexes(lo_heap_r);
581 
582  ScanKeyInit(&skey[0],
584  BTEqualStrategyNumber, F_OIDEQ,
585  ObjectIdGetDatum(obj_desc->id));
586 
587  ScanKeyInit(&skey[1],
590  Int32GetDatum(pageno));
591 
592  sd = systable_beginscan_ordered(lo_heap_r, lo_index_r,
593  obj_desc->snapshot, 2, skey);
594 
595  oldtuple = NULL;
596  olddata = NULL;
597  neednextpage = true;
598 
599  while (nwritten < nbytes)
600  {
601  /*
602  * If possible, get next pre-existing page of the LO. We expect the
603  * indexscan will deliver these in order --- but there may be holes.
604  */
605  if (neednextpage)
606  {
607  if ((oldtuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL)
608  {
609  if (HeapTupleHasNulls(oldtuple)) /* paranoia */
610  elog(ERROR, "null field found in pg_largeobject");
611  olddata = (Form_pg_largeobject) GETSTRUCT(oldtuple);
612  Assert(olddata->pageno >= pageno);
613  }
614  neednextpage = false;
615  }
616 
617  /*
618  * If we have a pre-existing page, see if it is the page we want to
619  * write, or a later one.
620  */
621  if (olddata != NULL && olddata->pageno == pageno)
622  {
623  /*
624  * Update an existing page with fresh data.
625  *
626  * First, load old data into workbuf
627  */
628  getdatafield(olddata, &datafield, &len, &pfreeit);
629  memcpy(workb, VARDATA(datafield), len);
630  if (pfreeit)
631  pfree(datafield);
632 
633  /*
634  * Fill any hole
635  */
636  off = (int) (obj_desc->offset % LOBLKSIZE);
637  if (off > len)
638  MemSet(workb + len, 0, off - len);
639 
640  /*
641  * Insert appropriate portion of new data
642  */
643  n = LOBLKSIZE - off;
644  n = (n <= (nbytes - nwritten)) ? n : (nbytes - nwritten);
645  memcpy(workb + off, buf + nwritten, n);
646  nwritten += n;
647  obj_desc->offset += n;
648  off += n;
649  /* compute valid length of new page */
650  len = (len >= off) ? len : off;
651  SET_VARSIZE(&workbuf.hdr, len + VARHDRSZ);
652 
653  /*
654  * Form and insert updated tuple
655  */
656  memset(values, 0, sizeof(values));
657  memset(nulls, false, sizeof(nulls));
658  memset(replace, false, sizeof(replace));
659  values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
660  replace[Anum_pg_largeobject_data - 1] = true;
661  newtup = heap_modify_tuple(oldtuple, RelationGetDescr(lo_heap_r),
662  values, nulls, replace);
663  CatalogTupleUpdateWithInfo(lo_heap_r, &newtup->t_self, newtup,
664  indstate);
665  heap_freetuple(newtup);
666 
667  /*
668  * We're done with this old page.
669  */
670  oldtuple = NULL;
671  olddata = NULL;
672  neednextpage = true;
673  }
674  else
675  {
676  /*
677  * Write a brand new page.
678  *
679  * First, fill any hole
680  */
681  off = (int) (obj_desc->offset % LOBLKSIZE);
682  if (off > 0)
683  MemSet(workb, 0, off);
684 
685  /*
686  * Insert appropriate portion of new data
687  */
688  n = LOBLKSIZE - off;
689  n = (n <= (nbytes - nwritten)) ? n : (nbytes - nwritten);
690  memcpy(workb + off, buf + nwritten, n);
691  nwritten += n;
692  obj_desc->offset += n;
693  /* compute valid length of new page */
694  len = off + n;
695  SET_VARSIZE(&workbuf.hdr, len + VARHDRSZ);
696 
697  /*
698  * Form and insert updated tuple
699  */
700  memset(values, 0, sizeof(values));
701  memset(nulls, false, sizeof(nulls));
702  values[Anum_pg_largeobject_loid - 1] = ObjectIdGetDatum(obj_desc->id);
703  values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno);
704  values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
705  newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls);
706  CatalogTupleInsertWithInfo(lo_heap_r, newtup, indstate);
707  heap_freetuple(newtup);
708  }
709  pageno++;
710  }
711 
713 
714  CatalogCloseIndexes(indstate);
715 
716  /*
717  * Advance command counter so that my tuple updates will be seen by later
718  * large-object operations in this transaction.
719  */
721 
722  return nwritten;
723 }
724 
725 void
726 inv_truncate(LargeObjectDesc *obj_desc, int64 len)
727 {
728  int32 pageno = (int32) (len / LOBLKSIZE);
729  int32 off;
730  ScanKeyData skey[2];
731  SysScanDesc sd;
732  HeapTuple oldtuple;
733  Form_pg_largeobject olddata;
734  union
735  {
736  bytea hdr;
737  /* this is to make the union big enough for a LO data chunk: */
738  char data[LOBLKSIZE + VARHDRSZ];
739  /* ensure union is aligned well enough: */
740  int32 align_it;
741  } workbuf;
742  char *workb = VARDATA(&workbuf.hdr);
743  HeapTuple newtup;
745  bool nulls[Natts_pg_largeobject];
746  bool replace[Natts_pg_largeobject];
747  CatalogIndexState indstate;
748 
749  Assert(PointerIsValid(obj_desc));
750 
751  /* enforce writability because snapshot is probably wrong otherwise */
752  Assert(obj_desc->flags & IFS_WRLOCK);
753 
754  /*
755  * use errmsg_internal here because we don't want to expose INT64_FORMAT
756  * in translatable strings; doing better is not worth the trouble
757  */
758  if (len < 0 || len > MAX_LARGE_OBJECT_SIZE)
759  ereport(ERROR,
760  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
761  errmsg_internal("invalid large object truncation target: " INT64_FORMAT,
762  len)));
763 
765 
766  indstate = CatalogOpenIndexes(lo_heap_r);
767 
768  /*
769  * Set up to find all pages with desired loid and pageno >= target
770  */
771  ScanKeyInit(&skey[0],
773  BTEqualStrategyNumber, F_OIDEQ,
774  ObjectIdGetDatum(obj_desc->id));
775 
776  ScanKeyInit(&skey[1],
779  Int32GetDatum(pageno));
780 
781  sd = systable_beginscan_ordered(lo_heap_r, lo_index_r,
782  obj_desc->snapshot, 2, skey);
783 
784  /*
785  * If possible, get the page the truncation point is in. The truncation
786  * point may be beyond the end of the LO or in a hole.
787  */
788  olddata = NULL;
789  if ((oldtuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL)
790  {
791  if (HeapTupleHasNulls(oldtuple)) /* paranoia */
792  elog(ERROR, "null field found in pg_largeobject");
793  olddata = (Form_pg_largeobject) GETSTRUCT(oldtuple);
794  Assert(olddata->pageno >= pageno);
795  }
796 
797  /*
798  * If we found the page of the truncation point we need to truncate the
799  * data in it. Otherwise if we're in a hole, we need to create a page to
800  * mark the end of data.
801  */
802  if (olddata != NULL && olddata->pageno == pageno)
803  {
804  /* First, load old data into workbuf */
805  bytea *datafield;
806  int pagelen;
807  bool pfreeit;
808 
809  getdatafield(olddata, &datafield, &pagelen, &pfreeit);
810  memcpy(workb, VARDATA(datafield), pagelen);
811  if (pfreeit)
812  pfree(datafield);
813 
814  /*
815  * Fill any hole
816  */
817  off = len % LOBLKSIZE;
818  if (off > pagelen)
819  MemSet(workb + pagelen, 0, off - pagelen);
820 
821  /* compute length of new page */
822  SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ);
823 
824  /*
825  * Form and insert updated tuple
826  */
827  memset(values, 0, sizeof(values));
828  memset(nulls, false, sizeof(nulls));
829  memset(replace, false, sizeof(replace));
830  values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
831  replace[Anum_pg_largeobject_data - 1] = true;
832  newtup = heap_modify_tuple(oldtuple, RelationGetDescr(lo_heap_r),
833  values, nulls, replace);
834  CatalogTupleUpdateWithInfo(lo_heap_r, &newtup->t_self, newtup,
835  indstate);
836  heap_freetuple(newtup);
837  }
838  else
839  {
840  /*
841  * If the first page we found was after the truncation point, we're in
842  * a hole that we'll fill, but we need to delete the later page
843  * because the loop below won't visit it again.
844  */
845  if (olddata != NULL)
846  {
847  Assert(olddata->pageno > pageno);
848  CatalogTupleDelete(lo_heap_r, &oldtuple->t_self);
849  }
850 
851  /*
852  * Write a brand new page.
853  *
854  * Fill the hole up to the truncation point
855  */
856  off = len % LOBLKSIZE;
857  if (off > 0)
858  MemSet(workb, 0, off);
859 
860  /* compute length of new page */
861  SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ);
862 
863  /*
864  * Form and insert new tuple
865  */
866  memset(values, 0, sizeof(values));
867  memset(nulls, false, sizeof(nulls));
868  values[Anum_pg_largeobject_loid - 1] = ObjectIdGetDatum(obj_desc->id);
869  values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno);
870  values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
871  newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls);
872  CatalogTupleInsertWithInfo(lo_heap_r, newtup, indstate);
873  heap_freetuple(newtup);
874  }
875 
876  /*
877  * Delete any pages after the truncation point. If the initial search
878  * didn't find a page, then of course there's nothing more to do.
879  */
880  if (olddata != NULL)
881  {
882  while ((oldtuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL)
883  {
884  CatalogTupleDelete(lo_heap_r, &oldtuple->t_self);
885  }
886  }
887 
889 
890  CatalogCloseIndexes(indstate);
891 
892  /*
893  * Advance command counter so that tuple updates will be seen by later
894  * large-object operations in this transaction.
895  */
897 }
static bool myLargeObjectExists(Oid loid, Snapshot snapshot)
Definition: inv_api.c:126
#define MAX_LARGE_OBJECT_SIZE
Definition: large_object.h:78
static void open_lo_relation(void)
Definition: inv_api.c:69
#define LOBLKSIZE
Definition: large_object.h:72
#define IFS_RDLOCK
Definition: large_object.h:48
int64 inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence)
Definition: inv_api.c:394
#define VARDATA(PTR)
Definition: postgres.h:303
void inv_truncate(LargeObjectDesc *obj_desc, int64 len)
Definition: inv_api.c:726
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:499
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:145
Oid inv_create(Oid lobjId)
Definition: inv_api.c:206
#define RelationGetDescr(relation)
Definition: rel.h:428
Oid GetUserId(void)
Definition: miscinit.c:284
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
ResourceOwner TopTransactionResourceOwner
Definition: resowner.c:140
#define VARSIZE(PTR)
Definition: postgres.h:304
#define PointerGetDatum(X)
Definition: postgres.h:562
#define VARHDRSZ
Definition: c.h:439
ResourceOwner CurrentResourceOwner
Definition: resowner.c:138
void inv_close(LargeObjectDesc *obj_desc)
Definition: inv_api.c:302
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:839
#define AccessShareLock
Definition: lockdefs.h:36
int errcode(int sqlerrcode)
Definition: elog.c:575
#define MemSet(start, val, len)
Definition: c.h:863
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:255
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:695
struct varlena * heap_tuple_untoast_attr(struct varlena *attr)
Definition: tuptoaster.c:172
#define heap_close(r, l)
Definition: heapam.h:97
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
Definition: pg_shdepend.c:159
Oid LargeObjectCreate(Oid loid)
HeapTuple systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction)
Definition: genam.c:597
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1373
unsigned int Oid
Definition: postgres_ext.h:31
SubTransactionId subid
Definition: large_object.h:43
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:328
static Relation lo_heap_r
Definition: inv_api.c:61
#define IFS_WRLOCK
Definition: large_object.h:49
signed int int32
Definition: c.h:246
int64 inv_tell(LargeObjectDesc *obj_desc)
Definition: inv_api.c:438
static Relation lo_index_r
Definition: inv_api.c:62
#define INV_READ
Definition: libpq-fs.h:22
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:416
void pfree(void *pointer)
Definition: mcxt.c:949
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
Snapshot snapshot
Definition: large_object.h:42
ItemPointerData t_self
Definition: htup.h:65
int inv_drop(Oid lobjId)
Definition: inv_api.c:318
#define HeapTupleHasNulls(tuple)
Definition: htup_details.h:662
#define LargeObjectLOidPNIndexId
Definition: indexing.h:180
#define NoLock
Definition: lockdefs.h:34
static char * buf
Definition: pg_test_fsync.c:67
#define RowExclusiveLock
Definition: lockdefs.h:38
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:303
#define ereport(elevel, rest)
Definition: elog.h:122
#define LargeObjectMetadataOidIndexId
Definition: indexing.h:183
void CatalogTupleUpdateWithInfo(Relation heapRel, ItemPointer otid, HeapTuple tup, CatalogIndexState indstate)
Definition: indexing.c:231
static uint64 inv_getsize(LargeObjectDesc *obj_desc)
Definition: inv_api.c:346
#define Natts_pg_largeobject
void close_lo_relation(bool isCommit)
Definition: inv_api.c:93
uintptr_t Datum
Definition: postgres.h:372
void CommandCounterIncrement(void)
Definition: xact.c:915
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
void systable_endscan_ordered(SysScanDesc sysscan)
Definition: genam.c:614
TupleDesc rd_att
Definition: rel.h:115
Snapshot RegisterSnapshotOnOwner(Snapshot snapshot, ResourceOwner owner)
Definition: snapmgr.c:876
int errmsg_internal(const char *fmt,...)
Definition: elog.c:827
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define Assert(condition)
Definition: c.h:681
static void getdatafield(Form_pg_largeobject tuple, bytea **pdatafield, int *plen, bool *pfreeit)
Definition: inv_api.c:164
SubTransactionId GetCurrentSubTransactionId(void)
Definition: xact.c:642
#define LargeObjectMetadataRelationId
CatalogIndexState CatalogOpenIndexes(Relation heapRel)
Definition: indexing.c:40
#define Anum_pg_largeobject_data
void UnregisterSnapshotFromOwner(Snapshot snapshot, ResourceOwner owner)
Definition: snapmgr.c:918
#define INT64_FORMAT
Definition: c.h:300
#define INV_WRITE
Definition: libpq-fs.h:21
#define Anum_pg_largeobject_pageno
#define VARATT_IS_EXTENDED(PTR)
Definition: postgres.h:326
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:176
static Datum values[MAXATTR]
Definition: bootstrap.c:164
#define Int32GetDatum(X)
Definition: postgres.h:485
LargeObjectDesc * inv_open(Oid lobjId, int flags, MemoryContext mcxt)
Definition: inv_api.c:247
int errmsg(const char *fmt,...)
Definition: elog.c:797
SysScanDesc systable_beginscan_ordered(Relation heapRelation, Relation indexRelation, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:533
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:706
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
void CatalogCloseIndexes(CatalogIndexState indstate)
Definition: indexing.c:58
Definition: c.h:433
#define SET_VARSIZE(PTR, len)
Definition: postgres.h:328
#define elog
Definition: elog.h:219
Oid CatalogTupleInsertWithInfo(Relation heapRel, HeapTuple tup, CatalogIndexState indstate)
Definition: indexing.c:186
FormData_pg_largeobject * Form_pg_largeobject
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, bool *replIsnull, bool *doReplace)
Definition: heaptuple.c:794
#define Anum_pg_largeobject_loid
int inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
Definition: inv_api.c:446
#define LargeObjectRelationId
#define PointerIsValid(pointer)
Definition: c.h:520
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:151
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32
int inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
Definition: inv_api.c:533