*** 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,3457 ----
     See also the aggregate function <function>string_agg</function> in
     <xref linkend="functions-aggregate">.
    </para>
+ 
+    <table id="functions-binarystring-largeobjects-transformations">
+     <title>Loading and saving from/to large objects Functions</title>
+     <tgroup cols="5">
+      <thead>
+       <row>
+        <entry>Function</entry>
+        <entry>Return Type</entry>
+        <entry>Description</entry>
+        <entry>Example</entry>
+        <entry>Result</entry>
+       </row>
+      </thead>
+ 
+      <tbody>
+       <row>
+        <entry>
+         <indexterm>
+          <primary>load_lo</primary>
+         </indexterm>
+         <literal><function>load_lo(<parameter>loid</parameter>
+         <type>oid</type>)</function></literal>
+        </entry>
+        <entry><type>bytea</type></entry>
+        <entry>
+         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.
+       </entry>
+       <entry><literal>load_lo(24628)</literal></entry>
+       <entry><literal>\xffffff00</literal></entry>
+      </row>
+ 
+      <row>
+       <entry>
+         <indexterm>
+          <primary>make_lo</primary>
+         </indexterm>
+        <literal><function>make_lo(<parameter>string</parameter> <type>bytea</type>,
+        <optional><parameter>loid</parameter> <type>oid</type></optional> )</function></literal>
+       </entry>
+       <entry><type>oid</type></entry>
+       <entry>
+        Create a large object and store a binary string there. Returns a oid of
+        created large object.
+       </entry>
+       <entry><literal>select make_lo(decode('ffffff00','hex'))</literal></entry>
+       <entry><literal>24528</literal></entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
   </sect1>
  
  
*** 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;
