2 -- Test large object support
4 -- directory paths are passed to us in environment variables
5 \getenv abs_srcdir PG_ABS_SRCDIR
6 \getenv abs_builddir PG_ABS_BUILDDIR
7 -- ensure consistent test output regardless of the default bytea format
8 SET bytea_output TO escape;
9 -- Test ALTER LARGE OBJECT OWNER
10 CREATE ROLE regress_lo_user;
17 ALTER LARGE OBJECT 42 OWNER TO regress_lo_user;
18 -- Test GRANT, COMMENT as non-superuser
19 SET SESSION AUTHORIZATION regress_lo_user;
20 GRANT SELECT ON LARGE OBJECT 42 TO public;
21 COMMENT ON LARGE OBJECT 42 IS 'the ultimate answer';
22 RESET SESSION AUTHORIZATION;
23 -- Test psql's \lo_list et al (we assume no other LOs exist yet)
26 ID | Owner | Description
27 ----+-----------------+---------------------
28 42 | regress_lo_user | the ultimate answer
33 ID | Owner | Access privileges | Description
34 ----+-----------------+------------------------------------+---------------------
35 42 | regress_lo_user | regress_lo_user=rw/regress_lo_user+| the ultimate answer
36 | | =r/regress_lo_user |
42 ID | Owner | Description
43 ----+-------+-------------
47 CREATE TABLE lotest_stash_values (loid oid, fd integer);
48 -- lo_creat(mode integer) returns oid
49 -- The mode arg to lo_creat is unused, some vestigal holdover from ancient times
50 -- returns the large object id
51 INSERT INTO lotest_stash_values (loid) SELECT lo_creat(42);
52 -- NOTE: large objects require transactions
54 -- lo_open(lobjId oid, mode integer) returns integer
55 -- The mode parameter to lo_open uses two constants:
56 -- INV_WRITE = 0x20000
58 -- The return value is a file descriptor-like value which remains valid for the
60 UPDATE lotest_stash_values SET fd = lo_open(loid, CAST(x'20000' | x'40000' AS integer));
61 -- loread/lowrite names are wonky, different from other functions which are lo_*
62 -- lowrite(fd integer, data bytea) returns integer
63 -- the integer is the number of bytes written
65 I wandered lonely as a cloud
66 That floats on high o''er vales and hills,
67 When all at once I saw a crowd,
68 A host, of golden daffodils;
69 Beside the lake, beneath the trees,
70 Fluttering and dancing in the breeze.
72 Continuous as the stars that shine
73 And twinkle on the milky way,
74 They stretched in never-ending line
75 Along the margin of a bay:
76 Ten thousand saw I at a glance,
77 Tossing their heads in sprightly dance.
79 The waves beside them danced; but they
80 Out-did the sparkling waves in glee:
81 A poet could not but be gay,
82 In such a jocund company:
83 I gazed--and gazed--but little thought
84 What wealth the show to me had brought:
86 For oft, when on my couch I lie
87 In vacant or in pensive mood,
88 They flash upon that inward eye
89 Which is the bliss of solitude;
90 And then my heart with pleasure fills,
91 And dances with the daffodils.
94 ') FROM lotest_stash_values;
100 -- lo_close(fd integer) returns integer
101 -- return value is 0 for success, or <0 for error (actually only -1, but...)
102 SELECT lo_close(fd) FROM lotest_stash_values;
109 -- Copy to another large object.
110 -- Note: we intentionally don't remove the object created here;
111 -- it's left behind to help test pg_dump.
112 SELECT lo_from_bytea(0, lo_get(loid)) AS newloid FROM lotest_stash_values
114 -- Add a comment to it, as well, for pg_dump/pg_upgrade testing.
115 COMMENT ON LARGE OBJECT :newloid IS 'I Wandered Lonely as a Cloud';
116 -- Read out a portion
118 UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer));
119 -- lo_lseek(fd integer, offset integer, whence integer) returns integer
120 -- offset is in bytes, whence is one of three values:
121 -- SEEK_SET (= 0) meaning relative to beginning
122 -- SEEK_CUR (= 1) meaning relative to current position
123 -- SEEK_END (= 2) meaning relative to end (offset better be negative)
124 -- returns current position in file
125 SELECT lo_lseek(fd, 104, 0) FROM lotest_stash_values;
131 -- loread/lowrite names are wonky, different from other functions which are lo_*
132 -- loread(fd integer, len integer) returns bytea
133 SELECT loread(fd, 28) FROM lotest_stash_values;
135 ------------------------------
136 A host, of golden daffodils;
139 SELECT lo_lseek(fd, -19, 1) FROM lotest_stash_values;
145 SELECT lowrite(fd, 'n') FROM lotest_stash_values;
151 SELECT lo_tell(fd) FROM lotest_stash_values;
157 SELECT lo_lseek(fd, -744, 2) FROM lotest_stash_values;
163 SELECT loread(fd, 28) FROM lotest_stash_values;
165 ------------------------------
166 A host, on golden daffodils;
169 SELECT lo_close(fd) FROM lotest_stash_values;
176 -- Test resource management
178 SELECT lo_open(loid, x'40000'::int) from lotest_stash_values;
185 \set filename :abs_builddir '/results/invalid/path'
186 \set dobody 'DECLARE loid oid; BEGIN '
187 \set dobody :dobody 'SELECT tbl.loid INTO loid FROM lotest_stash_values tbl; '
188 \set dobody :dobody 'PERFORM lo_export(loid, ' :'filename' '); '
189 \set dobody :dobody 'EXCEPTION WHEN UNDEFINED_FILE THEN '
190 \set dobody :dobody 'RAISE NOTICE ''could not open file, as expected''; END'
192 NOTICE: could not open file, as expected
195 UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer));
196 SELECT lo_truncate(fd, 11) FROM lotest_stash_values;
202 SELECT loread(fd, 15) FROM lotest_stash_values;
208 SELECT lo_truncate(fd, 10000) FROM lotest_stash_values;
214 SELECT loread(fd, 10) FROM lotest_stash_values;
216 ------------------------------------------
217 \000\000\000\000\000\000\000\000\000\000
220 SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
226 SELECT lo_tell(fd) FROM lotest_stash_values;
232 SELECT lo_truncate(fd, 5000) FROM lotest_stash_values;
238 SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
244 SELECT lo_tell(fd) FROM lotest_stash_values;
250 SELECT lo_close(fd) FROM lotest_stash_values;
257 -- Test 64-bit large object functions.
259 UPDATE lotest_stash_values SET fd = lo_open(loid, CAST(x'20000' | x'40000' AS integer));
260 SELECT lo_lseek64(fd, 4294967296, 0) FROM lotest_stash_values;
266 SELECT lowrite(fd, 'offset:4GB') FROM lotest_stash_values;
272 SELECT lo_tell64(fd) FROM lotest_stash_values;
278 SELECT lo_lseek64(fd, -10, 1) FROM lotest_stash_values;
284 SELECT lo_tell64(fd) FROM lotest_stash_values;
290 SELECT loread(fd, 10) FROM lotest_stash_values;
296 SELECT lo_truncate64(fd, 5000000000) FROM lotest_stash_values;
302 SELECT lo_lseek64(fd, 0, 2) FROM lotest_stash_values;
308 SELECT lo_tell64(fd) FROM lotest_stash_values;
314 SELECT lo_truncate64(fd, 3000000000) FROM lotest_stash_values;
320 SELECT lo_lseek64(fd, 0, 2) FROM lotest_stash_values;
326 SELECT lo_tell64(fd) FROM lotest_stash_values;
332 SELECT lo_close(fd) FROM lotest_stash_values;
339 -- lo_unlink(lobjId oid) returns integer
340 -- return value appears to always be 1
341 SELECT lo_unlink(loid) from lotest_stash_values;
347 TRUNCATE lotest_stash_values;
348 \set filename :abs_srcdir '/data/tenk.data'
349 INSERT INTO lotest_stash_values (loid) SELECT lo_import(:'filename');
351 UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer));
352 -- verify length of large object
353 SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
359 -- with the default BLCKSZ, LOBLKSIZE = 2048, so this positions us for a block
361 SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values;
367 -- this should get half of the value from page 0 and half from page 1 of the
369 SELECT loread(fd, 36) FROM lotest_stash_values;
371 --------------------------------------------------------------
372 44\011144\0111144\0114144\0119144\01188\01189\011SNAAAA\011F
375 SELECT lo_tell(fd) FROM lotest_stash_values;
381 SELECT lo_lseek(fd, -26, 1) FROM lotest_stash_values;
387 SELECT lowrite(fd, 'abcdefghijklmnop') FROM lotest_stash_values;
393 SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values;
399 SELECT loread(fd, 36) FROM lotest_stash_values;
401 --------------------------------------------------
402 44\011144\011114abcdefghijklmnop9\011SNAAAA\011F
405 SELECT lo_close(fd) FROM lotest_stash_values;
412 \set filename :abs_builddir '/results/lotest.txt'
413 SELECT lo_export(loid, :'filename') FROM lotest_stash_values;
420 \set newloid :LASTOID
421 -- just make sure \lo_export does not barf
422 \set filename :abs_builddir '/results/lotest2.txt'
423 \lo_export :newloid :filename
424 -- This is a hack to test that export/import are reversible
425 -- This uses knowledge about the inner workings of large object mechanism
426 -- which should not be used outside it. This makes it a HACK
427 SELECT pageno, data FROM pg_largeobject WHERE loid = (SELECT loid from lotest_stash_values)
429 SELECT pageno, data FROM pg_largeobject WHERE loid = :newloid;
434 SELECT lo_unlink(loid) FROM lotest_stash_values;
440 TRUNCATE lotest_stash_values;
442 \set filename :abs_builddir '/results/lotest.txt'
444 \set newloid_1 :LASTOID
445 SELECT lo_from_bytea(0, lo_get(:newloid_1)) AS newloid_2
447 SELECT fipshash(lo_get(:newloid_1)) = fipshash(lo_get(:newloid_2));
453 SELECT lo_get(:newloid_1, 0, 20);
455 -------------------------------------------
456 8800\0110\0110\0110\0110\0110\0110\011800
459 SELECT lo_get(:newloid_1, 10, 20);
461 -------------------------------------------
462 \0110\0110\0110\011800\011800\0113800\011
465 SELECT lo_put(:newloid_1, 5, decode('afafafaf', 'hex'));
471 SELECT lo_get(:newloid_1, 0, 20);
473 -------------------------------------------------
474 8800\011\257\257\257\2570\0110\0110\0110\011800
477 SELECT lo_put(:newloid_1, 4294967310, 'foo');
483 SELECT lo_get(:newloid_1);
484 ERROR: large object read request is too large
485 SELECT lo_get(:newloid_1, 4294967294, 100);
487 ---------------------------------------------------------------------
488 \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000foo
491 \lo_unlink :newloid_1
492 \lo_unlink :newloid_2
493 -- This object is left in the database for pg_dump test purposes
494 SELECT lo_from_bytea(0, E'\\xdeadbeef') AS newloid
496 SET bytea_output TO hex;
497 SELECT lo_get(:newloid);
503 -- Create one more object that we leave behind for testing pg_dump/pg_upgrade;
504 -- this one intentionally has an OID in the system range
505 SELECT lo_create(2121);
511 COMMENT ON LARGE OBJECT 2121 IS 'testing comments';
512 -- Test writes on large objects in read-only transactions
513 START TRANSACTION READ ONLY;
515 SELECT lo_open(2121, x'40000'::int);
521 -- INV_WRITE ... error
522 SELECT lo_open(2121, x'20000'::int);
523 ERROR: cannot execute lo_open(INV_WRITE) in a read-only transaction
525 START TRANSACTION READ ONLY;
526 SELECT lo_create(42);
527 ERROR: cannot execute lo_create() in a read-only transaction
529 START TRANSACTION READ ONLY;
531 ERROR: cannot execute lo_creat() in a read-only transaction
533 START TRANSACTION READ ONLY;
534 SELECT lo_unlink(42);
535 ERROR: cannot execute lo_unlink() in a read-only transaction
537 START TRANSACTION READ ONLY;
538 SELECT lowrite(42, 'x');
539 ERROR: cannot execute lowrite() in a read-only transaction
541 START TRANSACTION READ ONLY;
542 SELECT lo_import(:'filename');
543 ERROR: cannot execute lo_import() in a read-only transaction
545 START TRANSACTION READ ONLY;
546 SELECT lo_truncate(42, 0);
547 ERROR: cannot execute lo_truncate() in a read-only transaction
549 START TRANSACTION READ ONLY;
550 SELECT lo_truncate64(42, 0);
551 ERROR: cannot execute lo_truncate64() in a read-only transaction
553 START TRANSACTION READ ONLY;
554 SELECT lo_from_bytea(0, 'x');
555 ERROR: cannot execute lo_from_bytea() in a read-only transaction
557 START TRANSACTION READ ONLY;
558 SELECT lo_put(42, 0, 'x');
559 ERROR: cannot execute lo_put() in a read-only transaction
562 DROP TABLE lotest_stash_values;
563 DROP ROLE regress_lo_user;