*** a/doc/src/sgml/func.sgml --- b/doc/src/sgml/func.sgml *************** *** 3399,3404 **** SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); --- 3399,3462 ---- See also the aggregate function string_agg in . + + + Loading and saving from/to Large Objects Functions + + + + Function + Return Type + Description + Example + Result + + + + + + + + load_lo + + load_lo(loid + oid) + + bytea + + Returns a binary string based on content a entered large object. Attention: binary + string has lower size limit (1GB) than large objects (4GB). Processing very large + large object can be very expensive for memory resources. Bytea data are completly + holded in memomory. + + load_lo(24628) + \xffffff00 + + + + + + make_lo + + make_lo(string bytea, + loid oid ) + + oid + + Create a large object and store a binary string there. Returns a oid of + created large object. + + select make_lo(decode('ffffff00','hex')) + 24528 + + + +
+ + + See also a description of other Large Objects Function + in . + *** a/doc/src/sgml/lobj.sgml --- b/doc/src/sgml/lobj.sgml *************** *** 580,585 **** SELECT lo_export(image.raster, '/tmp/motd') FROM image --- 580,608 ---- these functions as loread and lowrite. + + There are other two functions , that doesn't correspond with client api + (see in ). + make_lo transforms a binary string to lo object, and + load_lo transforms a lo object to binary string. + + + + Some examples: + + SELECT make_lo(decode('ffffff00','hex')); + make_lo + --------- + 24629 + (1 row) + + SELECT load_lo(24628); + load_lo + ----------- + \xffffff00 + (1 row) + + *** a/src/backend/libpq/be-fsstubs.c --- b/src/backend/libpq/be-fsstubs.c *************** *** 754,756 **** deleteLOfd(int fd) --- 754,848 ---- { cookies[fd] = NULL; } + + /***************************************************************************** + * LO simplified SQL API for manipulation with LO + *****************************************************************************/ + + /* + * load LO and return it as bytea + */ + Datum + load_lo(PG_FUNCTION_ARGS) + { + Oid loOid = PG_GETARG_OID(0); + LargeObjectDesc *loDesc; + int64 len; + int total_read; + 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))); + + len = inv_seek(loDesc, 0, SEEK_END); + inv_seek(loDesc, 0, SEEK_SET); + + result = (bytea *) palloc(VARHDRSZ + len); + total_read = inv_read(loDesc, VARDATA(result), len); + + Assert(total_read == len); + + inv_close(loDesc); + + SET_VARSIZE(result, total_read + VARHDRSZ); + + PG_RETURN_BYTEA_P(result); + } + + /* + * internal - shared code for make_lo and make_lo_with_oid + */ + static Oid + make_lo_internal(bytea *str, Oid loOid) + { + LargeObjectDesc *loDesc; + + CreateFSContext(); + + loOid = inv_create(loOid); + loDesc = inv_open(loOid, INV_WRITE, fscxt); + inv_write(loDesc, VARDATA_ANY(str), + VARSIZE_ANY_EXHDR(str)); + inv_close(loDesc); + + return loOid; + } + + /* + * Save bytea to LO + */ + Datum + make_lo(PG_FUNCTION_ARGS) + { + bytea *str = PG_GETARG_BYTEA_PP(0); + + PG_RETURN_OID(make_lo_internal(str, InvalidOid)); + } + + /* + * Save bytea to LO with specified loOid + */ + Datum + make_lo_with_oid(PG_FUNCTION_ARGS) + { + bytea *str = PG_GETARG_BYTEA_PP(0); + Oid loOid = PG_GETARG_OID(1); + + PG_RETURN_OID(make_lo_internal(str, loOid)); + } *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** *** 1055,1060 **** DESCR("truncate large object"); --- 1055,1067 ---- 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_ )); DESCR("truncate large object (64 bit)"); + DATA(insert OID = 3178 ( load_lo PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 17 "26" _null_ _null_ _null_ _null_ load_lo _null_ _null_ _null_ )); + DESCR("load large object to bytea"); + DATA(insert OID = 3179 ( make_lo PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 26 "17" _null_ _null_ _null_ _null_ make_lo _null_ _null_ _null_ )); + DESCR("save bytea to large object"); + DATA(insert OID = 3180 ( make_lo PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 26 "17 26" _null_ _null_ _null_ _null_ make_lo_with_oid _null_ _null_ _null_ )); + DESCR("save bytea to large object"); + 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_ )); 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_ )); 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_ )); *** a/src/include/libpq/be-fsstubs.h --- b/src/include/libpq/be-fsstubs.h *************** *** 46,51 **** extern Datum lo_truncate64(PG_FUNCTION_ARGS); --- 46,58 ---- extern bool lo_compat_privileges; /* + * Simplified LO API + */ + extern Datum load_lo(PG_FUNCTION_ARGS); + extern Datum make_lo(PG_FUNCTION_ARGS); + extern Datum make_lo_with_oid(PG_FUNCTION_ARGS); + + /* * These are not fmgr-callable, but are available to C code. * Probably these should have had the underscore-free names, * but too late now... *** a/src/test/regress/input/largeobject.source --- b/src/test/regress/input/largeobject.source *************** *** 203,207 **** SELECT pageno, data FROM pg_largeobject WHERE loid = :newloid; --- 203,219 ---- SELECT lo_unlink(loid) FROM lotest_stash_values; \lo_unlink :newloid + \lo_import 'results/lotest.txt' + + \set newloid_1 :LASTOID + + SELECT make_lo(load_lo(:newloid_1)) AS newloid_2 + \gset + + SELECT md5(load_lo(:newloid_1)) = md5(load_lo(:newloid_2)); + + \lo_unlink :newloid_1 + \lo_unlink :newloid_2 + TRUNCATE lotest_stash_values; DROP ROLE regresslo; *** a/src/test/regress/output/largeobject.source --- b/src/test/regress/output/largeobject.source *************** *** 391,395 **** SELECT lo_unlink(loid) FROM lotest_stash_values; --- 391,407 ---- (1 row) \lo_unlink :newloid + \lo_import 'results/lotest.txt' + \set newloid_1 :LASTOID + SELECT make_lo(load_lo(:newloid_1)) AS newloid_2 + \gset + SELECT md5(load_lo(:newloid_1)) = md5(load_lo(:newloid_2)); + ?column? + ---------- + t + (1 row) + + \lo_unlink :newloid_1 + \lo_unlink :newloid_2 TRUNCATE lotest_stash_values; DROP ROLE regresslo; *** a/src/test/regress/output/largeobject_1.source --- b/src/test/regress/output/largeobject_1.source *************** *** 391,395 **** SELECT lo_unlink(loid) FROM lotest_stash_values; --- 391,407 ---- (1 row) \lo_unlink :newloid + \lo_import 'results/lotest.txt' + \set newloid_1 :LASTOID + SELECT make_lo(load_lo(:newloid_1)) AS newloid_2 + \gset + SELECT md5(load_lo(:newloid_1)) = md5(load_lo(:newloid_2)); + ?column? + ---------- + t + (1 row) + + \lo_unlink :newloid_1 + \lo_unlink :newloid_2 TRUNCATE lotest_stash_values; DROP ROLE regresslo;