aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorNoah Misch <noah@leadboat.com>2013-10-27 22:42:46 -0400
committerNoah Misch <noah@leadboat.com>2013-10-27 22:56:54 -0400
commitc50b7c09d852b6dc292bf24c72a0ffcac6cb2cab (patch)
treeee9cc909b6b1c7040c4c81e427d25d215e1d900f /src/backend
parent9c339eb4f853e44c462f53587f69e4a11e89c09b (diff)
downloadpostgresql-c50b7c09d852b6dc292bf24c72a0ffcac6cb2cab.tar.gz
postgresql-c50b7c09d852b6dc292bf24c72a0ffcac6cb2cab.zip
Add large object functions catering to SQL callers.
With these, one need no longer manipulate large object descriptors and extract numeric constants from header files in order to read and write large object contents from SQL. Pavel Stehule, reviewed by Rushabh Lathia.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/libpq/be-fsstubs.c151
1 files changed, 151 insertions, 0 deletions
diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c
index fa0038320b1..486d65fedf9 100644
--- a/src/backend/libpq/be-fsstubs.c
+++ b/src/backend/libpq/be-fsstubs.c
@@ -754,3 +754,154 @@ deleteLOfd(int fd)
{
cookies[fd] = NULL;
}
+
+/*****************************************************************************
+ * Wrappers oriented toward SQL callers
+ *****************************************************************************/
+
+/*
+ * Read [offset, offset+nbytes) within LO; when nbytes is -1, read to end.
+ */
+static bytea *
+lo_get_fragment_internal(Oid loOid, int64 offset, int32 nbytes)
+{
+ LargeObjectDesc *loDesc;
+ int64 loSize;
+ int64 result_length;
+ int total_read PG_USED_FOR_ASSERTS_ONLY;
+ bytea *result = NULL;
+
+ /*
+ * We don't actually need to store into fscxt, but create it anyway to
+ * ensure that AtEOXact_LargeObject knows there is state to clean up
+ */
+ CreateFSContext();
+
+ loDesc = inv_open(loOid, INV_READ, fscxt);
+
+ /* Permission check */
+ if (!lo_compat_privileges &&
+ pg_largeobject_aclcheck_snapshot(loDesc->id,
+ GetUserId(),
+ ACL_SELECT,
+ loDesc->snapshot) != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied for large object %u",
+ loDesc->id)));
+
+ /*
+ * Compute number of bytes we'll actually read, accommodating nbytes == -1
+ * and reads beyond the end of the LO.
+ */
+ loSize = inv_seek(loDesc, 0, SEEK_END);
+ if (loSize > offset)
+ {
+ if (nbytes >= 0 && nbytes <= loSize - offset)
+ result_length = nbytes; /* request is wholly inside LO */
+ else
+ result_length = loSize - offset; /* adjust to end of LO */
+ }
+ else
+ result_length = 0; /* request is wholly outside LO */
+
+ /*
+ * A result_length calculated from loSize may not fit in a size_t. Check
+ * that the size will satisfy this and subsequently-enforced size limits.
+ */
+ if (result_length > MaxAllocSize - VARHDRSZ)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("large object read request is too large")));
+
+ result = (bytea *) palloc(VARHDRSZ + result_length);
+
+ inv_seek(loDesc, offset, SEEK_SET);
+ total_read = inv_read(loDesc, VARDATA(result), result_length);
+ Assert(total_read == result_length);
+ SET_VARSIZE(result, result_length + VARHDRSZ);
+
+ inv_close(loDesc);
+
+ return result;
+}
+
+/*
+ * Read entire LO
+ */
+Datum
+lo_get(PG_FUNCTION_ARGS)
+{
+ Oid loOid = PG_GETARG_OID(0);
+ bytea *result;
+
+ result = lo_get_fragment_internal(loOid, 0, -1);
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+/*
+ * Read range within LO
+ */
+Datum
+lo_get_fragment(PG_FUNCTION_ARGS)
+{
+ Oid loOid = PG_GETARG_OID(0);
+ int64 offset = PG_GETARG_INT64(1);
+ int32 nbytes = PG_GETARG_INT32(2);
+ bytea *result;
+
+ if (nbytes < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("requested length cannot be negative")));
+
+ result = lo_get_fragment_internal(loOid, offset, nbytes);
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+/*
+ * Create LO with initial contents
+ */
+Datum
+lo_create_bytea(PG_FUNCTION_ARGS)
+{
+ Oid loOid = PG_GETARG_OID(0);
+ bytea *str = PG_GETARG_BYTEA_PP(1);
+ LargeObjectDesc *loDesc;
+ int written PG_USED_FOR_ASSERTS_ONLY;
+
+ CreateFSContext();
+
+ loOid = inv_create(loOid);
+ loDesc = inv_open(loOid, INV_WRITE, fscxt);
+ written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
+ Assert(written == VARSIZE_ANY_EXHDR(str));
+ inv_close(loDesc);
+
+ PG_RETURN_OID(loOid);
+}
+
+/*
+ * Update range within LO
+ */
+Datum
+lo_put(PG_FUNCTION_ARGS)
+{
+ Oid loOid = PG_GETARG_OID(0);
+ int64 offset = PG_GETARG_INT64(1);
+ bytea *str = PG_GETARG_BYTEA_PP(2);
+ LargeObjectDesc *loDesc;
+ int written PG_USED_FOR_ASSERTS_ONLY;
+
+ CreateFSContext();
+
+ loDesc = inv_open(loOid, INV_WRITE, fscxt);
+ inv_seek(loDesc, offset, SEEK_SET);
+ written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
+ Assert(written == VARSIZE_ANY_EXHDR(str));
+ inv_close(loDesc);
+
+ PG_RETURN_VOID();
+}