Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c50b7c0

Browse files
committed
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.
1 parent 9c339eb commit c50b7c0

File tree

9 files changed

+362
-7
lines changed

9 files changed

+362
-7
lines changed

doc/src/sgml/func.sgml

+2-1
Original file line numberDiff line numberDiff line change
@@ -3420,7 +3420,8 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
34203420

34213421
<para>
34223422
See also the aggregate function <function>string_agg</function> in
3423-
<xref linkend="functions-aggregate">.
3423+
<xref linkend="functions-aggregate"> and the large object functions
3424+
in <xref linkend="lo-funcs">.
34243425
</para>
34253426
</sect1>
34263427

doc/src/sgml/lobj.sgml

+73-5
Original file line numberDiff line numberDiff line change
@@ -526,11 +526,79 @@ int lo_unlink(PGconn *conn, Oid lobjId);
526526
<title>Server-side Functions</title>
527527

528528
<para>
529-
There are server-side functions callable from SQL that correspond to
530-
each of the client-side functions described above; indeed, for the
531-
most part the client-side functions are simply interfaces to the
532-
equivalent server-side functions. The ones that are actually useful
533-
to call via SQL commands are
529+
Server-side functions tailored for manipulating large objects from SQL are
530+
listed in <xref linkend="lo-funcs-table">.
531+
</para>
532+
533+
<table id="lo-funcs-table">
534+
<title>SQL-oriented Large Object Functions</title>
535+
<tgroup cols="5">
536+
<thead>
537+
<row>
538+
<entry>Function</entry>
539+
<entry>Return Type</entry>
540+
<entry>Description</entry>
541+
<entry>Example</entry>
542+
<entry>Result</entry>
543+
</row>
544+
</thead>
545+
546+
<tbody>
547+
<row>
548+
<entry>
549+
<indexterm>
550+
<primary>lo_create</primary>
551+
</indexterm>
552+
<literal><function>lo_create(<parameter>loid</parameter> <type>oid</type>, <parameter>string</parameter> <type>bytea</type>)</function></literal>
553+
</entry>
554+
<entry><type>oid</type></entry>
555+
<entry>
556+
Create a large object and store data there, returning its OID.
557+
Pass <literal>0</> to have the system choose an OID.
558+
</entry>
559+
<entry><literal>lo_create(0, E'\\xffffff00')</literal></entry>
560+
<entry><literal>24528</literal></entry>
561+
</row>
562+
563+
<row>
564+
<entry>
565+
<indexterm>
566+
<primary>lo_put</primary>
567+
</indexterm>
568+
<literal><function>lo_put(<parameter>loid</parameter> <type>oid</type>, <parameter>offset</parameter> <type>bigint</type>, <parameter>str</parameter> <type>bytea</type>)</function></literal>
569+
</entry>
570+
<entry><type>void</type></entry>
571+
<entry>
572+
Write data at the given offset.
573+
</entry>
574+
<entry><literal>lo_put(24528, 1, E'\\xaa')</literal></entry>
575+
<entry></entry>
576+
</row>
577+
578+
<row>
579+
<entry>
580+
<indexterm>
581+
<primary>lo_get</primary>
582+
</indexterm>
583+
<literal><function>lo_get(<parameter>loid</parameter> <type>oid</type> <optional>, <parameter>from</parameter> <type>bigint</type>, <parameter>for</parameter> <type>int</type></optional>)</function></literal>
584+
</entry>
585+
<entry><type>bytea</type></entry>
586+
<entry>
587+
Extract contents or a substring thereof.
588+
</entry>
589+
<entry><literal>lo_get(24528, 0, 3)</literal></entry>
590+
<entry><literal>\xffaaff</literal></entry>
591+
</row>
592+
593+
</tbody>
594+
</tgroup>
595+
</table>
596+
597+
<para>
598+
There are additional server-side functions corresponding to each of the
599+
client-side functions described earlier; indeed, for the most part the
600+
client-side functions are simply interfaces to the equivalent server-side
601+
functions. The ones just as convenient to call via SQL commands are
534602
<function>lo_creat</function><indexterm><primary>lo_creat</></>,
535603
<function>lo_create</function><indexterm><primary>lo_create</></>,
536604
<function>lo_unlink</function><indexterm><primary>lo_unlink</></>,

src/backend/libpq/be-fsstubs.c

+151
Original file line numberDiff line numberDiff line change
@@ -754,3 +754,154 @@ deleteLOfd(int fd)
754754
{
755755
cookies[fd] = NULL;
756756
}
757+
758+
/*****************************************************************************
759+
* Wrappers oriented toward SQL callers
760+
*****************************************************************************/
761+
762+
/*
763+
* Read [offset, offset+nbytes) within LO; when nbytes is -1, read to end.
764+
*/
765+
static bytea *
766+
lo_get_fragment_internal(Oid loOid, int64 offset, int32 nbytes)
767+
{
768+
LargeObjectDesc *loDesc;
769+
int64 loSize;
770+
int64 result_length;
771+
int total_read PG_USED_FOR_ASSERTS_ONLY;
772+
bytea *result = NULL;
773+
774+
/*
775+
* We don't actually need to store into fscxt, but create it anyway to
776+
* ensure that AtEOXact_LargeObject knows there is state to clean up
777+
*/
778+
CreateFSContext();
779+
780+
loDesc = inv_open(loOid, INV_READ, fscxt);
781+
782+
/* Permission check */
783+
if (!lo_compat_privileges &&
784+
pg_largeobject_aclcheck_snapshot(loDesc->id,
785+
GetUserId(),
786+
ACL_SELECT,
787+
loDesc->snapshot) != ACLCHECK_OK)
788+
ereport(ERROR,
789+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
790+
errmsg("permission denied for large object %u",
791+
loDesc->id)));
792+
793+
/*
794+
* Compute number of bytes we'll actually read, accommodating nbytes == -1
795+
* and reads beyond the end of the LO.
796+
*/
797+
loSize = inv_seek(loDesc, 0, SEEK_END);
798+
if (loSize > offset)
799+
{
800+
if (nbytes >= 0 && nbytes <= loSize - offset)
801+
result_length = nbytes; /* request is wholly inside LO */
802+
else
803+
result_length = loSize - offset; /* adjust to end of LO */
804+
}
805+
else
806+
result_length = 0; /* request is wholly outside LO */
807+
808+
/*
809+
* A result_length calculated from loSize may not fit in a size_t. Check
810+
* that the size will satisfy this and subsequently-enforced size limits.
811+
*/
812+
if (result_length > MaxAllocSize - VARHDRSZ)
813+
ereport(ERROR,
814+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
815+
errmsg("large object read request is too large")));
816+
817+
result = (bytea *) palloc(VARHDRSZ + result_length);
818+
819+
inv_seek(loDesc, offset, SEEK_SET);
820+
total_read = inv_read(loDesc, VARDATA(result), result_length);
821+
Assert(total_read == result_length);
822+
SET_VARSIZE(result, result_length + VARHDRSZ);
823+
824+
inv_close(loDesc);
825+
826+
return result;
827+
}
828+
829+
/*
830+
* Read entire LO
831+
*/
832+
Datum
833+
lo_get(PG_FUNCTION_ARGS)
834+
{
835+
Oid loOid = PG_GETARG_OID(0);
836+
bytea *result;
837+
838+
result = lo_get_fragment_internal(loOid, 0, -1);
839+
840+
PG_RETURN_BYTEA_P(result);
841+
}
842+
843+
/*
844+
* Read range within LO
845+
*/
846+
Datum
847+
lo_get_fragment(PG_FUNCTION_ARGS)
848+
{
849+
Oid loOid = PG_GETARG_OID(0);
850+
int64 offset = PG_GETARG_INT64(1);
851+
int32 nbytes = PG_GETARG_INT32(2);
852+
bytea *result;
853+
854+
if (nbytes < 0)
855+
ereport(ERROR,
856+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
857+
errmsg("requested length cannot be negative")));
858+
859+
result = lo_get_fragment_internal(loOid, offset, nbytes);
860+
861+
PG_RETURN_BYTEA_P(result);
862+
}
863+
864+
/*
865+
* Create LO with initial contents
866+
*/
867+
Datum
868+
lo_create_bytea(PG_FUNCTION_ARGS)
869+
{
870+
Oid loOid = PG_GETARG_OID(0);
871+
bytea *str = PG_GETARG_BYTEA_PP(1);
872+
LargeObjectDesc *loDesc;
873+
int written PG_USED_FOR_ASSERTS_ONLY;
874+
875+
CreateFSContext();
876+
877+
loOid = inv_create(loOid);
878+
loDesc = inv_open(loOid, INV_WRITE, fscxt);
879+
written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
880+
Assert(written == VARSIZE_ANY_EXHDR(str));
881+
inv_close(loDesc);
882+
883+
PG_RETURN_OID(loOid);
884+
}
885+
886+
/*
887+
* Update range within LO
888+
*/
889+
Datum
890+
lo_put(PG_FUNCTION_ARGS)
891+
{
892+
Oid loOid = PG_GETARG_OID(0);
893+
int64 offset = PG_GETARG_INT64(1);
894+
bytea *str = PG_GETARG_BYTEA_PP(2);
895+
LargeObjectDesc *loDesc;
896+
int written PG_USED_FOR_ASSERTS_ONLY;
897+
898+
CreateFSContext();
899+
900+
loDesc = inv_open(loOid, INV_WRITE, fscxt);
901+
inv_seek(loDesc, offset, SEEK_SET);
902+
written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
903+
Assert(written == VARSIZE_ANY_EXHDR(str));
904+
inv_close(loDesc);
905+
906+
PG_RETURN_VOID();
907+
}

src/include/catalog/catversion.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 201310101
56+
#define CATALOG_VERSION_NO 201310271
5757

5858
#endif

src/include/catalog/pg_proc.h

+9
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,15 @@ DESCR("truncate large object");
10551055
DATA(insert OID = 3172 ( lo_truncate64 PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 23 "23 20" _null_ _null_ _null_ _null_ lo_truncate64 _null_ _null_ _null_ ));
10561056
DESCR("truncate large object (64 bit)");
10571057

1058+
DATA(insert OID = 3457 ( lo_create PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 26 "26 17" _null_ _null_ _null_ _null_ lo_create_bytea _null_ _null_ _null_ ));
1059+
DESCR("create new large object with content");
1060+
DATA(insert OID = 3458 ( lo_get PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 17 "26" _null_ _null_ _null_ _null_ lo_get _null_ _null_ _null_ ));
1061+
DESCR("read entire large object");
1062+
DATA(insert OID = 3459 ( lo_get PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 17 "26 20 23" _null_ _null_ _null_ _null_ lo_get_fragment _null_ _null_ _null_ ));
1063+
DESCR("read large object from offset for length");
1064+
DATA(insert OID = 3460 ( lo_put PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 2278 "26 20 17" _null_ _null_ _null_ _null_ lo_put _null_ _null_ _null_ ));
1065+
DESCR("write data at offset");
1066+
10581067
DATA(insert OID = 959 ( on_pl PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "600 628" _null_ _null_ _null_ _null_ on_pl _null_ _null_ _null_ ));
10591068
DATA(insert OID = 960 ( on_sl PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "601 628" _null_ _null_ _null_ _null_ on_sl _null_ _null_ _null_ ));
10601069
DATA(insert OID = 961 ( close_pl PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 600 "600 628" _null_ _null_ _null_ _null_ close_pl _null_ _null_ _null_ ));

src/include/libpq/be-fsstubs.h

+5
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,18 @@ extern Datum lo_export(PG_FUNCTION_ARGS);
2525

2626
extern Datum lo_creat(PG_FUNCTION_ARGS);
2727
extern Datum lo_create(PG_FUNCTION_ARGS);
28+
extern Datum lo_create_bytea(PG_FUNCTION_ARGS);
2829

2930
extern Datum lo_open(PG_FUNCTION_ARGS);
3031
extern Datum lo_close(PG_FUNCTION_ARGS);
3132

3233
extern Datum loread(PG_FUNCTION_ARGS);
3334
extern Datum lowrite(PG_FUNCTION_ARGS);
3435

36+
extern Datum lo_get(PG_FUNCTION_ARGS);
37+
extern Datum lo_get_fragment(PG_FUNCTION_ARGS);
38+
extern Datum lo_put(PG_FUNCTION_ARGS);
39+
3540
extern Datum lo_lseek(PG_FUNCTION_ARGS);
3641
extern Datum lo_tell(PG_FUNCTION_ARGS);
3742
extern Datum lo_lseek64(PG_FUNCTION_ARGS);

src/test/regress/input/largeobject.source

+21
Original file line numberDiff line numberDiff line change
@@ -203,5 +203,26 @@ SELECT pageno, data FROM pg_largeobject WHERE loid = :newloid;
203203
SELECT lo_unlink(loid) FROM lotest_stash_values;
204204
\lo_unlink :newloid
205205

206+
\lo_import 'results/lotest.txt'
207+
208+
\set newloid_1 :LASTOID
209+
210+
SELECT lo_create(0, lo_get(:newloid_1)) AS newloid_2
211+
\gset
212+
213+
SELECT md5(lo_get(:newloid_1)) = md5(lo_get(:newloid_2));
214+
215+
SELECT lo_get(:newloid_1, 0, 20);
216+
SELECT lo_get(:newloid_1, 10, 20);
217+
SELECT lo_put(:newloid_1, 5, decode('afafafaf', 'hex'));
218+
SELECT lo_get(:newloid_1, 0, 20);
219+
220+
SELECT lo_put(:newloid_1, 4294967310, 'foo');
221+
SELECT lo_get(:newloid_1);
222+
SELECT lo_get(:newloid_1, 4294967294, 100);
223+
224+
\lo_unlink :newloid_1
225+
\lo_unlink :newloid_2
226+
206227
TRUNCATE lotest_stash_values;
207228
DROP ROLE regresslo;

src/test/regress/output/largeobject.source

+50
Original file line numberDiff line numberDiff line change
@@ -391,5 +391,55 @@ SELECT lo_unlink(loid) FROM lotest_stash_values;
391391
(1 row)
392392

393393
\lo_unlink :newloid
394+
\lo_import 'results/lotest.txt'
395+
\set newloid_1 :LASTOID
396+
SELECT lo_create(0, lo_get(:newloid_1)) AS newloid_2
397+
\gset
398+
SELECT md5(lo_get(:newloid_1)) = md5(lo_get(:newloid_2));
399+
?column?
400+
----------
401+
t
402+
(1 row)
403+
404+
SELECT lo_get(:newloid_1, 0, 20);
405+
lo_get
406+
-------------------------------------------
407+
8800\0110\0110\0110\0110\0110\0110\011800
408+
(1 row)
409+
410+
SELECT lo_get(:newloid_1, 10, 20);
411+
lo_get
412+
-------------------------------------------
413+
\0110\0110\0110\011800\011800\0113800\011
414+
(1 row)
415+
416+
SELECT lo_put(:newloid_1, 5, decode('afafafaf', 'hex'));
417+
lo_put
418+
--------
419+
420+
(1 row)
421+
422+
SELECT lo_get(:newloid_1, 0, 20);
423+
lo_get
424+
-------------------------------------------------
425+
8800\011\257\257\257\2570\0110\0110\0110\011800
426+
(1 row)
427+
428+
SELECT lo_put(:newloid_1, 4294967310, 'foo');
429+
lo_put
430+
--------
431+
432+
(1 row)
433+
434+
SELECT lo_get(:newloid_1);
435+
ERROR: large object read request is too large
436+
SELECT lo_get(:newloid_1, 4294967294, 100);
437+
lo_get
438+
---------------------------------------------------------------------
439+
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000foo
440+
(1 row)
441+
442+
\lo_unlink :newloid_1
443+
\lo_unlink :newloid_2
394444
TRUNCATE lotest_stash_values;
395445
DROP ROLE regresslo;

0 commit comments

Comments
 (0)