PostgreSQL Source Code  git master
be-fsstubs.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * be-fsstubs.c
4  * Builtin functions for open/close/read/write operations on large objects
5  *
6  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  * src/backend/libpq/be-fsstubs.c
12  *
13  * NOTES
14  * This should be moved to a more appropriate place. It is here
15  * for lack of a better place.
16  *
17  * These functions store LargeObjectDesc structs in a private MemoryContext,
18  * which means that large object descriptors hang around until we destroy
19  * the context at transaction end. It'd be possible to prolong the lifetime
20  * of the context so that LO FDs are good across transactions (for example,
21  * we could release the context only if we see that no FDs remain open).
22  * But we'd need additional state in order to do the right thing at the
23  * end of an aborted transaction. FDs opened during an aborted xact would
24  * still need to be closed, since they might not be pointing at valid
25  * relations at all. Locking semantics are also an interesting problem
26  * if LOs stay open across transactions. For now, we'll stick with the
27  * existing documented semantics of LO FDs: they're only good within a
28  * transaction.
29  *
30  * As of PostgreSQL 8.0, much of the angst expressed above is no longer
31  * relevant, and in fact it'd be pretty easy to allow LO FDs to stay
32  * open across transactions. (Snapshot relevancy would still be an issue.)
33  * However backwards compatibility suggests that we should stick to the
34  * status quo.
35  *
36  *-------------------------------------------------------------------------
37  */
38 
39 #include "postgres.h"
40 
41 #include <fcntl.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44 
45 #include "access/xact.h"
47 #include "libpq/be-fsstubs.h"
48 #include "libpq/libpq-fs.h"
49 #include "miscadmin.h"
50 #include "storage/fd.h"
51 #include "storage/large_object.h"
52 #include "utils/acl.h"
53 #include "utils/builtins.h"
54 #include "utils/memutils.h"
55 #include "utils/snapmgr.h"
56 #include "varatt.h"
57 
58 /* define this to enable debug logging */
59 /* #define FSDB 1 */
60 /* chunk size for lo_import/lo_export transfers */
61 #define BUFSIZE 8192
62 
63 /*
64  * LO "FD"s are indexes into the cookies array.
65  *
66  * A non-null entry is a pointer to a LargeObjectDesc allocated in the
67  * LO private memory context "fscxt". The cookies array itself is also
68  * dynamically allocated in that context. Its current allocated size is
69  * cookies_size entries, of which any unused entries will be NULL.
70  */
71 static LargeObjectDesc **cookies = NULL;
72 static int cookies_size = 0;
73 
74 static bool lo_cleanup_needed = false;
75 static MemoryContext fscxt = NULL;
76 
77 static int newLOfd(void);
78 static void closeLOfd(int fd);
79 static Oid lo_import_internal(text *filename, Oid lobjOid);
80 
81 
82 /*****************************************************************************
83  * File Interfaces for Large Objects
84  *****************************************************************************/
85 
86 Datum
88 {
89  Oid lobjId = PG_GETARG_OID(0);
91  LargeObjectDesc *lobjDesc;
92  int fd;
93 
94 #ifdef FSDB
95  elog(DEBUG4, "lo_open(%u,%d)", lobjId, mode);
96 #endif
97 
98  if (mode & INV_WRITE)
99  PreventCommandIfReadOnly("lo_open(INV_WRITE)");
100 
101  /*
102  * Allocate a large object descriptor first. This will also create
103  * 'fscxt' if this is the first LO opened in this transaction.
104  */
105  fd = newLOfd();
106 
107  lobjDesc = inv_open(lobjId, mode, fscxt);
108  lobjDesc->subid = GetCurrentSubTransactionId();
109 
110  /*
111  * We must register the snapshot in TopTransaction's resowner so that it
112  * stays alive until the LO is closed rather than until the current portal
113  * shuts down.
114  */
115  if (lobjDesc->snapshot)
116  lobjDesc->snapshot = RegisterSnapshotOnOwner(lobjDesc->snapshot,
118 
119  Assert(cookies[fd] == NULL);
120  cookies[fd] = lobjDesc;
121 
123 }
124 
125 Datum
127 {
128  int32 fd = PG_GETARG_INT32(0);
129 
130  if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
131  ereport(ERROR,
132  (errcode(ERRCODE_UNDEFINED_OBJECT),
133  errmsg("invalid large-object descriptor: %d", fd)));
134 
135 #ifdef FSDB
136  elog(DEBUG4, "lo_close(%d)", fd);
137 #endif
138 
139  closeLOfd(fd);
140 
141  PG_RETURN_INT32(0);
142 }
143 
144 
145 /*****************************************************************************
146  * Bare Read/Write operations --- these are not fmgr-callable!
147  *
148  * We assume the large object supports byte oriented reads and seeks so
149  * that our work is easier.
150  *
151  *****************************************************************************/
152 
153 int
154 lo_read(int fd, char *buf, int len)
155 {
156  int status;
157  LargeObjectDesc *lobj;
158 
159  if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
160  ereport(ERROR,
161  (errcode(ERRCODE_UNDEFINED_OBJECT),
162  errmsg("invalid large-object descriptor: %d", fd)));
163  lobj = cookies[fd];
164 
165  /*
166  * Check state. inv_read() would throw an error anyway, but we want the
167  * error to be about the FD's state not the underlying privilege; it might
168  * be that the privilege exists but user forgot to ask for read mode.
169  */
170  if ((lobj->flags & IFS_RDLOCK) == 0)
171  ereport(ERROR,
172  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
173  errmsg("large object descriptor %d was not opened for reading",
174  fd)));
175 
176  status = inv_read(lobj, buf, len);
177 
178  return status;
179 }
180 
181 int
182 lo_write(int fd, const char *buf, int len)
183 {
184  int status;
185  LargeObjectDesc *lobj;
186 
187  if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
188  ereport(ERROR,
189  (errcode(ERRCODE_UNDEFINED_OBJECT),
190  errmsg("invalid large-object descriptor: %d", fd)));
191  lobj = cookies[fd];
192 
193  /* see comment in lo_read() */
194  if ((lobj->flags & IFS_WRLOCK) == 0)
195  ereport(ERROR,
196  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
197  errmsg("large object descriptor %d was not opened for writing",
198  fd)));
199 
200  status = inv_write(lobj, buf, len);
201 
202  return status;
203 }
204 
205 Datum
207 {
208  int32 fd = PG_GETARG_INT32(0);
209  int32 offset = PG_GETARG_INT32(1);
210  int32 whence = PG_GETARG_INT32(2);
211  int64 status;
212 
213  if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
214  ereport(ERROR,
215  (errcode(ERRCODE_UNDEFINED_OBJECT),
216  errmsg("invalid large-object descriptor: %d", fd)));
217 
218  status = inv_seek(cookies[fd], offset, whence);
219 
220  /* guard against result overflow */
221  if (status != (int32) status)
222  ereport(ERROR,
223  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
224  errmsg("lo_lseek result out of range for large-object descriptor %d",
225  fd)));
226 
228 }
229 
230 Datum
232 {
233  int32 fd = PG_GETARG_INT32(0);
234  int64 offset = PG_GETARG_INT64(1);
235  int32 whence = PG_GETARG_INT32(2);
236  int64 status;
237 
238  if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
239  ereport(ERROR,
240  (errcode(ERRCODE_UNDEFINED_OBJECT),
241  errmsg("invalid large-object descriptor: %d", fd)));
242 
243  status = inv_seek(cookies[fd], offset, whence);
244 
246 }
247 
248 Datum
250 {
251  Oid lobjId;
252 
253  PreventCommandIfReadOnly("lo_creat()");
254 
255  lo_cleanup_needed = true;
256  lobjId = inv_create(InvalidOid);
257 
258  PG_RETURN_OID(lobjId);
259 }
260 
261 Datum
263 {
264  Oid lobjId = PG_GETARG_OID(0);
265 
266  PreventCommandIfReadOnly("lo_create()");
267 
268  lo_cleanup_needed = true;
269  lobjId = inv_create(lobjId);
270 
271  PG_RETURN_OID(lobjId);
272 }
273 
274 Datum
276 {
277  int32 fd = PG_GETARG_INT32(0);
278  int64 offset;
279 
280  if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
281  ereport(ERROR,
282  (errcode(ERRCODE_UNDEFINED_OBJECT),
283  errmsg("invalid large-object descriptor: %d", fd)));
284 
285  offset = inv_tell(cookies[fd]);
286 
287  /* guard against result overflow */
288  if (offset != (int32) offset)
289  ereport(ERROR,
290  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
291  errmsg("lo_tell result out of range for large-object descriptor %d",
292  fd)));
293 
294  PG_RETURN_INT32((int32) offset);
295 }
296 
297 Datum
299 {
300  int32 fd = PG_GETARG_INT32(0);
301  int64 offset;
302 
303  if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
304  ereport(ERROR,
305  (errcode(ERRCODE_UNDEFINED_OBJECT),
306  errmsg("invalid large-object descriptor: %d", fd)));
307 
308  offset = inv_tell(cookies[fd]);
309 
310  PG_RETURN_INT64(offset);
311 }
312 
313 Datum
315 {
316  Oid lobjId = PG_GETARG_OID(0);
317 
318  PreventCommandIfReadOnly("lo_unlink()");
319 
320  /*
321  * Must be owner of the large object. It would be cleaner to check this
322  * in inv_drop(), but we want to throw the error before not after closing
323  * relevant FDs.
324  */
325  if (!lo_compat_privileges &&
326  !object_ownercheck(LargeObjectMetadataRelationId, lobjId, GetUserId()))
327  ereport(ERROR,
328  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
329  errmsg("must be owner of large object %u", lobjId)));
330 
331  /*
332  * If there are any open LO FDs referencing that ID, close 'em.
333  */
334  if (fscxt != NULL)
335  {
336  int i;
337 
338  for (i = 0; i < cookies_size; i++)
339  {
340  if (cookies[i] != NULL && cookies[i]->id == lobjId)
341  closeLOfd(i);
342  }
343  }
344 
345  /*
346  * inv_drop does not create a need for end-of-transaction cleanup and
347  * hence we don't need to set lo_cleanup_needed.
348  */
349  PG_RETURN_INT32(inv_drop(lobjId));
350 }
351 
352 /*****************************************************************************
353  * Read/Write using bytea
354  *****************************************************************************/
355 
356 Datum
358 {
359  int32 fd = PG_GETARG_INT32(0);
361  bytea *retval;
362  int totalread;
363 
364  if (len < 0)
365  len = 0;
366 
367  retval = (bytea *) palloc(VARHDRSZ + len);
368  totalread = lo_read(fd, VARDATA(retval), len);
369  SET_VARSIZE(retval, totalread + VARHDRSZ);
370 
371  PG_RETURN_BYTEA_P(retval);
372 }
373 
374 Datum
376 {
377  int32 fd = PG_GETARG_INT32(0);
378  bytea *wbuf = PG_GETARG_BYTEA_PP(1);
379  int bytestowrite;
380  int totalwritten;
381 
382  PreventCommandIfReadOnly("lowrite()");
383 
384  bytestowrite = VARSIZE_ANY_EXHDR(wbuf);
385  totalwritten = lo_write(fd, VARDATA_ANY(wbuf), bytestowrite);
386  PG_RETURN_INT32(totalwritten);
387 }
388 
389 /*****************************************************************************
390  * Import/Export of Large Object
391  *****************************************************************************/
392 
393 /*
394  * lo_import -
395  * imports a file as an (inversion) large object.
396  */
397 Datum
399 {
401 
403 }
404 
405 /*
406  * lo_import_with_oid -
407  * imports a file as an (inversion) large object specifying oid.
408  */
409 Datum
411 {
413  Oid oid = PG_GETARG_OID(1);
414 
416 }
417 
418 static Oid
420 {
421  int fd;
422  int nbytes,
424  char buf[BUFSIZE];
425  char fnamebuf[MAXPGPATH];
426  LargeObjectDesc *lobj;
427  Oid oid;
428 
429  PreventCommandIfReadOnly("lo_import()");
430 
431  /*
432  * open the file to be read in
433  */
434  text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
435  fd = OpenTransientFile(fnamebuf, O_RDONLY | PG_BINARY);
436  if (fd < 0)
437  ereport(ERROR,
439  errmsg("could not open server file \"%s\": %m",
440  fnamebuf)));
441 
442  /*
443  * create an inversion object
444  */
445  lo_cleanup_needed = true;
446  oid = inv_create(lobjOid);
447 
448  /*
449  * read in from the filesystem and write to the inversion object
450  */
452 
453  while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
454  {
455  tmp = inv_write(lobj, buf, nbytes);
456  Assert(tmp == nbytes);
457  }
458 
459  if (nbytes < 0)
460  ereport(ERROR,
462  errmsg("could not read server file \"%s\": %m",
463  fnamebuf)));
464 
465  inv_close(lobj);
466 
467  if (CloseTransientFile(fd) != 0)
468  ereport(ERROR,
470  errmsg("could not close file \"%s\": %m",
471  fnamebuf)));
472 
473  return oid;
474 }
475 
476 /*
477  * lo_export -
478  * exports an (inversion) large object.
479  */
480 Datum
482 {
483  Oid lobjId = PG_GETARG_OID(0);
485  int fd;
486  int nbytes,
487  tmp;
488  char buf[BUFSIZE];
489  char fnamebuf[MAXPGPATH];
490  LargeObjectDesc *lobj;
491  mode_t oumask;
492 
493  /*
494  * open the inversion object (no need to test for failure)
495  */
496  lo_cleanup_needed = true;
497  lobj = inv_open(lobjId, INV_READ, CurrentMemoryContext);
498 
499  /*
500  * open the file to be written to
501  *
502  * Note: we reduce backend's normal 077 umask to the slightly friendlier
503  * 022. This code used to drop it all the way to 0, but creating
504  * world-writable export files doesn't seem wise.
505  */
506  text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
507  oumask = umask(S_IWGRP | S_IWOTH);
508  PG_TRY();
509  {
510  fd = OpenTransientFilePerm(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY,
512  }
513  PG_FINALLY();
514  {
515  umask(oumask);
516  }
517  PG_END_TRY();
518  if (fd < 0)
519  ereport(ERROR,
521  errmsg("could not create server file \"%s\": %m",
522  fnamebuf)));
523 
524  /*
525  * read in from the inversion file and write to the filesystem
526  */
527  while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
528  {
529  tmp = write(fd, buf, nbytes);
530  if (tmp != nbytes)
531  ereport(ERROR,
533  errmsg("could not write server file \"%s\": %m",
534  fnamebuf)));
535  }
536 
537  if (CloseTransientFile(fd) != 0)
538  ereport(ERROR,
540  errmsg("could not close file \"%s\": %m",
541  fnamebuf)));
542 
543  inv_close(lobj);
544 
545  PG_RETURN_INT32(1);
546 }
547 
548 /*
549  * lo_truncate -
550  * truncate a large object to a specified length
551  */
552 static void
554 {
555  LargeObjectDesc *lobj;
556 
557  if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
558  ereport(ERROR,
559  (errcode(ERRCODE_UNDEFINED_OBJECT),
560  errmsg("invalid large-object descriptor: %d", fd)));
561  lobj = cookies[fd];
562 
563  /* see comment in lo_read() */
564  if ((lobj->flags & IFS_WRLOCK) == 0)
565  ereport(ERROR,
566  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
567  errmsg("large object descriptor %d was not opened for writing",
568  fd)));
569 
570  inv_truncate(lobj, len);
571 }
572 
573 Datum
575 {
576  int32 fd = PG_GETARG_INT32(0);
578 
579  PreventCommandIfReadOnly("lo_truncate()");
580 
582  PG_RETURN_INT32(0);
583 }
584 
585 Datum
587 {
588  int32 fd = PG_GETARG_INT32(0);
589  int64 len = PG_GETARG_INT64(1);
590 
591  PreventCommandIfReadOnly("lo_truncate64()");
592 
594  PG_RETURN_INT32(0);
595 }
596 
597 /*
598  * AtEOXact_LargeObject -
599  * prepares large objects for transaction commit
600  */
601 void
602 AtEOXact_LargeObject(bool isCommit)
603 {
604  int i;
605 
606  if (!lo_cleanup_needed)
607  return; /* no LO operations in this xact */
608 
609  /*
610  * Close LO fds and clear cookies array so that LO fds are no longer good.
611  * The memory context and resource owner holding them are going away at
612  * the end-of-transaction anyway, but on commit, we need to close them to
613  * avoid warnings about leaked resources at commit. On abort we can skip
614  * this step.
615  */
616  if (isCommit)
617  {
618  for (i = 0; i < cookies_size; i++)
619  {
620  if (cookies[i] != NULL)
621  closeLOfd(i);
622  }
623  }
624 
625  /* Needn't actually pfree since we're about to zap context */
626  cookies = NULL;
627  cookies_size = 0;
628 
629  /* Release the LO memory context to prevent permanent memory leaks. */
630  if (fscxt)
632  fscxt = NULL;
633 
634  /* Give inv_api.c a chance to clean up, too */
635  close_lo_relation(isCommit);
636 
637  lo_cleanup_needed = false;
638 }
639 
640 /*
641  * AtEOSubXact_LargeObject
642  * Take care of large objects at subtransaction commit/abort
643  *
644  * Reassign LOs created/opened during a committing subtransaction
645  * to the parent subtransaction. On abort, just close them.
646  */
647 void
649  SubTransactionId parentSubid)
650 {
651  int i;
652 
653  if (fscxt == NULL) /* no LO operations in this xact */
654  return;
655 
656  for (i = 0; i < cookies_size; i++)
657  {
658  LargeObjectDesc *lo = cookies[i];
659 
660  if (lo != NULL && lo->subid == mySubid)
661  {
662  if (isCommit)
663  lo->subid = parentSubid;
664  else
665  closeLOfd(i);
666  }
667  }
668 }
669 
670 /*****************************************************************************
671  * Support routines for this file
672  *****************************************************************************/
673 
674 static int
675 newLOfd(void)
676 {
677  int i,
678  newsize;
679 
680  lo_cleanup_needed = true;
681  if (fscxt == NULL)
683  "Filesystem",
685 
686  /* Try to find a free slot */
687  for (i = 0; i < cookies_size; i++)
688  {
689  if (cookies[i] == NULL)
690  return i;
691  }
692 
693  /* No free slot, so make the array bigger */
694  if (cookies_size <= 0)
695  {
696  /* First time through, arbitrarily make 64-element array */
697  i = 0;
698  newsize = 64;
699  cookies = (LargeObjectDesc **)
700  MemoryContextAllocZero(fscxt, newsize * sizeof(LargeObjectDesc *));
701  }
702  else
703  {
704  /* Double size of array */
705  i = cookies_size;
706  newsize = cookies_size * 2;
707  cookies =
709  }
710  cookies_size = newsize;
711 
712  return i;
713 }
714 
715 static void
717 {
718  LargeObjectDesc *lobj;
719 
720  /*
721  * Make sure we do not try to free twice if this errors out for some
722  * reason. Better a leak than a crash.
723  */
724  lobj = cookies[fd];
725  cookies[fd] = NULL;
726 
727  if (lobj->snapshot)
730  inv_close(lobj);
731 }
732 
733 /*****************************************************************************
734  * Wrappers oriented toward SQL callers
735  *****************************************************************************/
736 
737 /*
738  * Read [offset, offset+nbytes) within LO; when nbytes is -1, read to end.
739  */
740 static bytea *
741 lo_get_fragment_internal(Oid loOid, int64 offset, int32 nbytes)
742 {
743  LargeObjectDesc *loDesc;
744  int64 loSize;
745  int64 result_length;
746  int total_read PG_USED_FOR_ASSERTS_ONLY;
747  bytea *result = NULL;
748 
749  lo_cleanup_needed = true;
750  loDesc = inv_open(loOid, INV_READ, CurrentMemoryContext);
751 
752  /*
753  * Compute number of bytes we'll actually read, accommodating nbytes == -1
754  * and reads beyond the end of the LO.
755  */
756  loSize = inv_seek(loDesc, 0, SEEK_END);
757  if (loSize > offset)
758  {
759  if (nbytes >= 0 && nbytes <= loSize - offset)
760  result_length = nbytes; /* request is wholly inside LO */
761  else
762  result_length = loSize - offset; /* adjust to end of LO */
763  }
764  else
765  result_length = 0; /* request is wholly outside LO */
766 
767  /*
768  * A result_length calculated from loSize may not fit in a size_t. Check
769  * that the size will satisfy this and subsequently-enforced size limits.
770  */
771  if (result_length > MaxAllocSize - VARHDRSZ)
772  ereport(ERROR,
773  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
774  errmsg("large object read request is too large")));
775 
776  result = (bytea *) palloc(VARHDRSZ + result_length);
777 
778  inv_seek(loDesc, offset, SEEK_SET);
779  total_read = inv_read(loDesc, VARDATA(result), result_length);
780  Assert(total_read == result_length);
781  SET_VARSIZE(result, result_length + VARHDRSZ);
782 
783  inv_close(loDesc);
784 
785  return result;
786 }
787 
788 /*
789  * Read entire LO
790  */
791 Datum
793 {
794  Oid loOid = PG_GETARG_OID(0);
795  bytea *result;
796 
797  result = lo_get_fragment_internal(loOid, 0, -1);
798 
799  PG_RETURN_BYTEA_P(result);
800 }
801 
802 /*
803  * Read range within LO
804  */
805 Datum
807 {
808  Oid loOid = PG_GETARG_OID(0);
809  int64 offset = PG_GETARG_INT64(1);
810  int32 nbytes = PG_GETARG_INT32(2);
811  bytea *result;
812 
813  if (nbytes < 0)
814  ereport(ERROR,
815  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
816  errmsg("requested length cannot be negative")));
817 
818  result = lo_get_fragment_internal(loOid, offset, nbytes);
819 
820  PG_RETURN_BYTEA_P(result);
821 }
822 
823 /*
824  * Create LO with initial contents given by a bytea argument
825  */
826 Datum
828 {
829  Oid loOid = PG_GETARG_OID(0);
831  LargeObjectDesc *loDesc;
832  int written PG_USED_FOR_ASSERTS_ONLY;
833 
834  PreventCommandIfReadOnly("lo_from_bytea()");
835 
836  lo_cleanup_needed = true;
837  loOid = inv_create(loOid);
838  loDesc = inv_open(loOid, INV_WRITE, CurrentMemoryContext);
839  written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
840  Assert(written == VARSIZE_ANY_EXHDR(str));
841  inv_close(loDesc);
842 
843  PG_RETURN_OID(loOid);
844 }
845 
846 /*
847  * Update range within LO
848  */
849 Datum
851 {
852  Oid loOid = PG_GETARG_OID(0);
853  int64 offset = PG_GETARG_INT64(1);
855  LargeObjectDesc *loDesc;
856  int written PG_USED_FOR_ASSERTS_ONLY;
857 
858  PreventCommandIfReadOnly("lo_put()");
859 
860  lo_cleanup_needed = true;
861  loDesc = inv_open(loOid, INV_WRITE, CurrentMemoryContext);
862 
863  /* Permission check */
864  if (!lo_compat_privileges &&
866  GetUserId(),
867  ACL_UPDATE,
868  loDesc->snapshot) != ACLCHECK_OK)
869  ereport(ERROR,
870  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
871  errmsg("permission denied for large object %u",
872  loDesc->id)));
873 
874  inv_seek(loDesc, offset, SEEK_SET);
875  written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
876  Assert(written == VARSIZE_ANY_EXHDR(str));
877  inv_close(loDesc);
878 
879  PG_RETURN_VOID();
880 }
@ ACLCHECK_OK
Definition: acl.h:183
AclResult pg_largeobject_aclcheck_snapshot(Oid lobj_oid, Oid roleid, AclMode mode, Snapshot snapshot)
Definition: aclchk.c:3970
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:3984
Datum be_lo_lseek(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:206
Datum be_loread(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:357
static MemoryContext fscxt
Definition: be-fsstubs.c:75
Datum be_lo_get(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:792
static void closeLOfd(int fd)
Definition: be-fsstubs.c:716
Datum be_lo_export(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:481
void AtEOXact_LargeObject(bool isCommit)
Definition: be-fsstubs.c:602
Datum be_lo_unlink(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:314
Datum be_lo_import_with_oid(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:410
Datum be_lo_truncate64(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:586
Datum be_lo_lseek64(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:231
Datum be_lo_tell64(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:298
int lo_write(int fd, const char *buf, int len)
Definition: be-fsstubs.c:182
Datum be_lo_from_bytea(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:827
int lo_read(int fd, char *buf, int len)
Definition: be-fsstubs.c:154
static Oid lo_import_internal(text *filename, Oid lobjOid)
Definition: be-fsstubs.c:419
Datum be_lowrite(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:375
static int newLOfd(void)
Definition: be-fsstubs.c:675
Datum be_lo_creat(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:249
Datum be_lo_put(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:850
Datum be_lo_truncate(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:574
Datum be_lo_create(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:262
static bool lo_cleanup_needed
Definition: be-fsstubs.c:74
static bytea * lo_get_fragment_internal(Oid loOid, int64 offset, int32 nbytes)
Definition: be-fsstubs.c:741
Datum be_lo_tell(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:275
Datum be_lo_import(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:398
Datum be_lo_get_fragment(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:806
static LargeObjectDesc ** cookies
Definition: be-fsstubs.c:71
Datum be_lo_close(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:126
static void lo_truncate_internal(int32 fd, int64 len)
Definition: be-fsstubs.c:553
static int cookies_size
Definition: be-fsstubs.c:72
void AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid)
Definition: be-fsstubs.c:648
#define BUFSIZE
Definition: be-fsstubs.c:61
Datum be_lo_open(PG_FUNCTION_ARGS)
Definition: be-fsstubs.c:87
uint32 SubTransactionId
Definition: c.h:640
signed int int32
Definition: c.h:478
#define VARHDRSZ
Definition: c.h:676
#define PG_BINARY
Definition: c.h:1260
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:166
int errcode_for_file_access(void)
Definition: elog.c:881
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define PG_TRY(...)
Definition: elog.h:370
#define PG_END_TRY(...)
Definition: elog.h:395
#define ERROR
Definition: elog.h:39
#define PG_FINALLY(...)
Definition: elog.h:387
#define ereport(elevel,...)
Definition: elog.h:149
#define DEBUG4
Definition: elog.h:27
int CloseTransientFile(int fd)
Definition: fd.c:2609
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2433
int OpenTransientFilePerm(const char *fileName, int fileFlags, mode_t fileMode)
Definition: fd.c:2442
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_GETARG_BYTEA_PP(n)
Definition: fmgr.h:308
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:371
#define PG_RETURN_INT64(x)
Definition: fmgr.h:368
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283
#define PG_RETURN_INT32(x)
Definition: fmgr.h:354
#define PG_GETARG_INT32(n)
Definition: fmgr.h:269
#define PG_RETURN_OID(x)
Definition: fmgr.h:360
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define write(a, b, c)
Definition: win32.h:14
#define read(a, b, c)
Definition: win32.h:13
void inv_truncate(LargeObjectDesc *obj_desc, int64 len)
Definition: inv_api.c:780
LargeObjectDesc * inv_open(Oid lobjId, int flags, MemoryContext mcxt)
Definition: inv_api.c:255
int inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
Definition: inv_api.c:490
Oid inv_create(Oid lobjId)
Definition: inv_api.c:212
int64 inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence)
Definition: inv_api.c:428
void close_lo_relation(bool isCommit)
Definition: inv_api.c:99
int inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
Definition: inv_api.c:583
int64 inv_tell(LargeObjectDesc *obj_desc)
Definition: inv_api.c:477
bool lo_compat_privileges
Definition: inv_api.c:58
void inv_close(LargeObjectDesc *obj_desc)
Definition: inv_api.c:339
int inv_drop(Oid lobjId)
Definition: inv_api.c:351
int i
Definition: isn.c:73
#define IFS_RDLOCK
Definition: large_object.h:48
#define IFS_WRLOCK
Definition: large_object.h:49
#define INV_READ
Definition: libpq-fs.h:22
#define INV_WRITE
Definition: libpq-fs.h:21
Assert(fmt[strlen(fmt) - 1] !='\n')
MemoryContext TopMemoryContext
Definition: mcxt.c:141
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1048
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:387
void * palloc(Size size)
Definition: mcxt.c:1210
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:153
#define MaxAllocSize
Definition: memutils.h:40
Oid GetUserId(void)
Definition: miscinit.c:502
#define repalloc0_array(pointer, type, oldcount, count)
Definition: palloc.h:110
#define ACL_UPDATE
Definition: parsenodes.h:85
static PgChecksumMode mode
Definition: pg_checksums.c:65
#define MAXPGPATH
const void size_t len
static char * filename
Definition: pg_dumpall.c:119
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:224
static char * buf
Definition: pg_test_fsync.c:67
uintptr_t Datum
Definition: postgres.h:64
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
static int fd(const char *x, int i)
Definition: preproc-init.c:105
ResourceOwner TopTransactionResourceOwner
Definition: resowner.c:148
void UnregisterSnapshotFromOwner(Snapshot snapshot, ResourceOwner owner)
Definition: snapmgr.c:884
Snapshot RegisterSnapshotOnOwner(Snapshot snapshot, ResourceOwner owner)
Definition: snapmgr.c:842
Snapshot snapshot
Definition: large_object.h:42
SubTransactionId subid
Definition: large_object.h:43
Definition: c.h:671
void PreventCommandIfReadOnly(const char *cmdname)
Definition: utility.c:411
#define VARDATA(PTR)
Definition: varatt.h:278
#define VARDATA_ANY(PTR)
Definition: varatt.h:324
#define SET_VARSIZE(PTR, len)
Definition: varatt.h:305
#define VARSIZE_ANY_EXHDR(PTR)
Definition: varatt.h:317
void text_to_cstring_buffer(const text *src, char *dst, size_t dst_len)
Definition: varlena.c:253
#define S_IROTH
Definition: win32_port.h:315
#define S_IRGRP
Definition: win32_port.h:303
#define S_IWOTH
Definition: win32_port.h:318
#define S_IRUSR
Definition: win32_port.h:291
#define S_IWUSR
Definition: win32_port.h:294
#define S_IWGRP
Definition: win32_port.h:306
SubTransactionId GetCurrentSubTransactionId(void)
Definition: xact.c:780