--
-- Test large object support
--
-- Load a file
CREATE TABLE lotest_stash_values (loid oid, fd integer);
-- lo_creat(mode integer) returns oid
-- The mode arg to lo_creat is unused, some vestigal holdover from ancient times
-- returns the large object id
INSERT INTO lotest_stash_values (loid) SELECT lo_creat(42);
-- NOTE: large objects require transactions
BEGIN;
-- lo_open(lobjId oid, mode integer) returns integer
-- The mode parameter to lo_open uses two constants:
--   INV_READ  = 0x20000
--   INV_WRITE = 0x40000
-- The return value is a file descriptor-like value which remains valid for the
-- transaction.
UPDATE lotest_stash_values SET fd = lo_open(loid, CAST(x'20000' | x'40000' AS integer));
-- loread/lowrite names are wonky, different from other functions which are lo_*
-- lowrite(fd integer, data bytea) returns integer
-- the integer is the number of bytes written
SELECT lowrite(fd, '
I wandered lonely as a cloud
That floats on high o''er vales and hills,
When all at once I saw a crowd,
A host, of golden daffodils;
Beside the lake, beneath the trees,
Fluttering and dancing in the breeze.

Continuous as the stars that shine
And twinkle on the milky way,
They stretched in never-ending line
Along the margin of a bay:
Ten thousand saw I at a glance,
Tossing their heads in sprightly dance.

The waves beside them danced; but they
Out-did the sparkling waves in glee:
A poet could not but be gay,
In such a jocund company:
I gazed--and gazed--but little thought
What wealth the show to me had brought:

For oft, when on my couch I lie
In vacant or in pensive mood,
They flash upon that inward eye
Which is the bliss of solitude;
And then my heart with pleasure fills,
And dances with the daffodils.

         -- William Wordsworth
') FROM lotest_stash_values;
 lowrite 
---------
     848
(1 row)

-- lo_close(fd integer) returns integer
-- return value is 0 for success, or <0 for error (actually only -1, but...)
SELECT lo_close(fd) FROM lotest_stash_values;
 lo_close 
----------
        0
(1 row)

END;
-- Read out a portion
BEGIN;
UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer));
-- lo_lseek(fd integer, offset integer, whence integer) returns integer
-- offset is in bytes, whence is one of three values:
--  SEEK_SET (= 0) meaning relative to beginning
--  SEEK_CUR (= 1) meaning relative to current position
--  SEEK_END (= 2) meaning relative to end (offset better be negative)
-- returns current position in file
SELECT lo_lseek(fd, 104, 0) FROM lotest_stash_values;
 lo_lseek 
----------
      104
(1 row)

-- loread/lowrite names are wonky, different from other functions which are lo_*
-- loread(fd integer, len integer) returns bytea
SELECT loread(fd, 28) FROM lotest_stash_values;
            loread            
------------------------------
 A host, of golden daffodils;
(1 row)

SELECT lo_lseek(fd, -19, 1) FROM lotest_stash_values;
 lo_lseek 
----------
      113
(1 row)

SELECT lowrite(fd, 'n') FROM lotest_stash_values;
 lowrite 
---------
       1
(1 row)

SELECT lo_tell(fd) FROM lotest_stash_values;
 lo_tell 
---------
     114
(1 row)

SELECT lo_lseek(fd, -744, 2) FROM lotest_stash_values;
 lo_lseek 
----------
      104
(1 row)

SELECT loread(fd, 28) FROM lotest_stash_values;
            loread            
------------------------------
 A host, on golden daffodils;
(1 row)

SELECT lo_close(fd) FROM lotest_stash_values;
 lo_close 
----------
        0
(1 row)

END;
-- Test resource management
BEGIN;
SELECT lo_open(loid, x'40000'::int) from lotest_stash_values;
 lo_open 
---------
       0
(1 row)

ABORT;
-- Test truncation.
BEGIN;
UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer));
SELECT lo_truncate(fd, 11) FROM lotest_stash_values;
 lo_truncate 
-------------
           0
(1 row)

SELECT loread(fd, 15) FROM lotest_stash_values;
     loread     
----------------
 \012I wandered
(1 row)

SELECT lo_truncate(fd, 10000) FROM lotest_stash_values;
 lo_truncate 
-------------
           0
(1 row)

SELECT loread(fd, 10) FROM lotest_stash_values;
                  loread                  
------------------------------------------
 \000\000\000\000\000\000\000\000\000\000
(1 row)

SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
 lo_lseek 
----------
    10000
(1 row)

SELECT lo_tell(fd) FROM lotest_stash_values;
 lo_tell 
---------
   10000
(1 row)

SELECT lo_truncate(fd, 5000) FROM lotest_stash_values;
 lo_truncate 
-------------
           0
(1 row)

SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
 lo_lseek 
----------
     5000
(1 row)

SELECT lo_tell(fd) FROM lotest_stash_values;
 lo_tell 
---------
    5000
(1 row)

SELECT lo_close(fd) FROM lotest_stash_values;
 lo_close 
----------
        0
(1 row)

END;
-- lo_unlink(lobjId oid) returns integer
-- return value appears to always be 1
SELECT lo_unlink(loid) from lotest_stash_values;
 lo_unlink 
-----------
         1
(1 row)

TRUNCATE lotest_stash_values;
INSERT INTO lotest_stash_values (loid) SELECT lo_import('@abs_srcdir@/data/tenk.data');
BEGIN;
UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer));
-- with the default BLKSZ, LOBLKSZ = 2048, so this positions us for a block
-- edge case
SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values;
 lo_lseek 
----------
     2030
(1 row)

-- this should get half of the value from page 0 and half from page 1 of the
-- large object
SELECT loread(fd, 36) FROM lotest_stash_values;
                             loread                              
-----------------------------------------------------------------
 AAA\011FBAAAA\011VVVVxx\0122513\01132\0111\0111\0113\01113\0111
(1 row)

SELECT lo_tell(fd) FROM lotest_stash_values;
 lo_tell 
---------
    2066
(1 row)

SELECT lo_lseek(fd, -26, 1) FROM lotest_stash_values;
 lo_lseek 
----------
     2040
(1 row)

SELECT lowrite(fd, 'abcdefghijklmnop') FROM lotest_stash_values;
 lowrite 
---------
      16
(1 row)

SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values;
 lo_lseek 
----------
     2030
(1 row)

SELECT loread(fd, 36) FROM lotest_stash_values;
                       loread                        
-----------------------------------------------------
 AAA\011FBAAAAabcdefghijklmnop1\0111\0113\01113\0111
(1 row)

SELECT lo_close(fd) FROM lotest_stash_values;
 lo_close 
----------
        0
(1 row)

END;
SELECT lo_export(loid, '@abs_builddir@/results/lotest.txt') FROM lotest_stash_values;
 lo_export 
-----------
         1
(1 row)

\lo_import 'results/lotest.txt'
\set newloid :LASTOID
-- just make sure \lo_export does not barf
\lo_export :newloid 'results/lotest2.txt'
-- This is a hack to test that export/import are reversible
-- This uses knowledge about the inner workings of large object mechanism
-- which should not be used outside it.  This makes it a HACK
SELECT pageno, data FROM pg_largeobject WHERE loid = (SELECT loid from lotest_stash_values)
EXCEPT
SELECT pageno, data FROM pg_largeobject WHERE loid = :newloid;
 pageno | data 
--------+------
(0 rows)

SELECT lo_unlink(loid) FROM lotest_stash_values;
 lo_unlink 
-----------
         1
(1 row)

\lo_unlink :newloid
TRUNCATE lotest_stash_values;
