mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Add battery of tests related TOAST tables with oid8
The tests added by this commit cover a large ground in terms of the introduction of oid8 in TOAST tables, with the following areas covered, with all the gaps found during the development of the feature: - Tables with oid8 TOAST tables in general, under strings.sql. - ALTER TABLE with attribute manipulations in transactions and different toast_table_value settings. - Relation rewrites, not affecting the TOAST relation after initial creation with VACUUM FULL, CLUSTER, REPACK. - pg_column_compression(). - test_decoding, for external and extended TOAST data. - amcheck, external and extended storage with heap checked. - UPDATE with out-of-line datums that belongs to another TOAST table. This offers coverage for toast_tuple_init(), where external vartags could be mixed once multiple on-disk external TOAST pointers can co-exist. This pattern can be triggered with asan/ubsan, with an out-of-bound memcpy when unpatched. These tests are added before the new vartag_external, mostly as a matter of avoiding dead code in the tree. Author: Michael Paquier <michael@paquier.xyz> Reviewed-by: Greg Burd <greg@burd.me> Reviewed-by: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> Reviewed-by: Yugo Nagata <nagata@sraoss.co.jp> Discussion: https://postgr.es/m/af19kUjwjhaoUTLn@paquier.xy
This commit is contained in:
@@ -199,6 +199,44 @@ SELECT * FROM verify_heapam('test_partition',
|
||||
-------+--------+--------+-----
|
||||
(0 rows)
|
||||
|
||||
-- Check TOAST relations of both chunk_id: oid and oid8
|
||||
CREATE TABLE test_toast_oid (a int, b text) WITH (toast_value_type = 'oid');
|
||||
CREATE TABLE test_toast_oid8 (a int, b text) WITH (toast_value_type = 'oid8');
|
||||
-- Uncompressed out-of-line values
|
||||
ALTER TABLE test_toast_oid ALTER COLUMN b SET STORAGE EXTERNAL;
|
||||
ALTER TABLE test_toast_oid8 ALTER COLUMN b SET STORAGE EXTERNAL;
|
||||
INSERT INTO test_toast_oid (a, b)
|
||||
(SELECT gs, repeat('xyzzy', 20000) FROM generate_series(1,5) gs);
|
||||
INSERT INTO test_toast_oid8 (a, b)
|
||||
(SELECT gs, repeat('xyzzy', 20000) FROM generate_series(1,5) gs);
|
||||
-- Compressed out-of-line values.
|
||||
ALTER TABLE test_toast_oid ALTER COLUMN b SET STORAGE EXTENDED;
|
||||
ALTER TABLE test_toast_oid8 ALTER COLUMN b SET STORAGE EXTENDED;
|
||||
INSERT INTO test_toast_oid (a, b)
|
||||
(SELECT gs, repeat('xyzzy', 20000) FROM generate_series(6,10) gs);
|
||||
INSERT INTO test_toast_oid8 (a, b)
|
||||
(SELECT gs, repeat('xyzzy', 20000) FROM generate_series(6,10) gs);
|
||||
SELECT c.relname, a.atttypid::regtype AS chunk_id_type
|
||||
FROM pg_class AS c, pg_attribute AS a
|
||||
WHERE c.relname IN ('test_toast_oid', 'test_toast_oid8') AND
|
||||
a.attrelid = c.reltoastrelid AND a.attname = 'chunk_id'
|
||||
ORDER BY c.relname COLLATE "C";
|
||||
relname | chunk_id_type
|
||||
-----------------+---------------
|
||||
test_toast_oid | oid
|
||||
test_toast_oid8 | oid8
|
||||
(2 rows)
|
||||
|
||||
SELECT * FROM verify_heapam('test_toast_oid', check_toast := true);
|
||||
blkno | offnum | attnum | msg
|
||||
-------+--------+--------+-----
|
||||
(0 rows)
|
||||
|
||||
SELECT * FROM verify_heapam('test_toast_oid8', check_toast := true);
|
||||
blkno | offnum | attnum | msg
|
||||
-------+--------+--------+-----
|
||||
(0 rows)
|
||||
|
||||
-- Check that indexes are rejected
|
||||
CREATE INDEX test_index ON test_partition (a);
|
||||
SELECT * FROM verify_heapam('test_index',
|
||||
@@ -232,6 +270,8 @@ SELECT * FROM verify_heapam('test_foreign_table',
|
||||
ERROR: cannot check relation "test_foreign_table"
|
||||
DETAIL: This operation is not supported for foreign tables.
|
||||
-- cleanup
|
||||
DROP TABLE test_toast_oid;
|
||||
DROP TABLE test_toast_oid8;
|
||||
DROP TABLE heaptest;
|
||||
DROP TABLESPACE regress_test_stats_tblspc;
|
||||
DROP TABLE test_partition;
|
||||
|
||||
@@ -112,6 +112,31 @@ SELECT * FROM verify_heapam('test_partition',
|
||||
startblock := NULL,
|
||||
endblock := NULL);
|
||||
|
||||
-- Check TOAST relations of both chunk_id: oid and oid8
|
||||
CREATE TABLE test_toast_oid (a int, b text) WITH (toast_value_type = 'oid');
|
||||
CREATE TABLE test_toast_oid8 (a int, b text) WITH (toast_value_type = 'oid8');
|
||||
-- Uncompressed out-of-line values
|
||||
ALTER TABLE test_toast_oid ALTER COLUMN b SET STORAGE EXTERNAL;
|
||||
ALTER TABLE test_toast_oid8 ALTER COLUMN b SET STORAGE EXTERNAL;
|
||||
INSERT INTO test_toast_oid (a, b)
|
||||
(SELECT gs, repeat('xyzzy', 20000) FROM generate_series(1,5) gs);
|
||||
INSERT INTO test_toast_oid8 (a, b)
|
||||
(SELECT gs, repeat('xyzzy', 20000) FROM generate_series(1,5) gs);
|
||||
-- Compressed out-of-line values.
|
||||
ALTER TABLE test_toast_oid ALTER COLUMN b SET STORAGE EXTENDED;
|
||||
ALTER TABLE test_toast_oid8 ALTER COLUMN b SET STORAGE EXTENDED;
|
||||
INSERT INTO test_toast_oid (a, b)
|
||||
(SELECT gs, repeat('xyzzy', 20000) FROM generate_series(6,10) gs);
|
||||
INSERT INTO test_toast_oid8 (a, b)
|
||||
(SELECT gs, repeat('xyzzy', 20000) FROM generate_series(6,10) gs);
|
||||
SELECT c.relname, a.atttypid::regtype AS chunk_id_type
|
||||
FROM pg_class AS c, pg_attribute AS a
|
||||
WHERE c.relname IN ('test_toast_oid', 'test_toast_oid8') AND
|
||||
a.attrelid = c.reltoastrelid AND a.attname = 'chunk_id'
|
||||
ORDER BY c.relname COLLATE "C";
|
||||
SELECT * FROM verify_heapam('test_toast_oid', check_toast := true);
|
||||
SELECT * FROM verify_heapam('test_toast_oid8', check_toast := true);
|
||||
|
||||
-- Check that indexes are rejected
|
||||
CREATE INDEX test_index ON test_partition (a);
|
||||
SELECT * FROM verify_heapam('test_index',
|
||||
@@ -139,6 +164,8 @@ SELECT * FROM verify_heapam('test_foreign_table',
|
||||
endblock := NULL);
|
||||
|
||||
-- cleanup
|
||||
DROP TABLE test_toast_oid;
|
||||
DROP TABLE test_toast_oid8;
|
||||
DROP TABLE heaptest;
|
||||
DROP TABLESPACE regress_test_stats_tblspc;
|
||||
DROP TABLE test_partition;
|
||||
|
||||
@@ -382,6 +382,45 @@ SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot',
|
||||
COMMIT
|
||||
(4 rows)
|
||||
|
||||
-- Test decoding of TOAST values with oid8
|
||||
CREATE TABLE toasted_oid8 (id serial primary key, data text)
|
||||
WITH (toast_value_type = 'oid8');
|
||||
-- uncompressed external toast data
|
||||
ALTER TABLE toasted_oid8 ALTER COLUMN data SET STORAGE EXTERNAL;
|
||||
INSERT INTO toasted_oid8(data) VALUES (repeat('1234567890', 20000));
|
||||
-- compressed external toast data
|
||||
ALTER TABLE toasted_oid8 ALTER COLUMN data SET STORAGE EXTENDED;
|
||||
INSERT INTO toasted_oid8(data) VALUES (repeat('1234567890', 20000));
|
||||
-- update without changing the toasted column, reported as unchanged
|
||||
UPDATE toasted_oid8 SET id = id + 10 WHERE id = 1;
|
||||
-- update changing the toasted column
|
||||
UPDATE toasted_oid8 SET data = repeat('abcdefghij', 20000) WHERE id = 2;
|
||||
DELETE FROM toasted_oid8;
|
||||
-- Check that the values are reassembled in full.
|
||||
SELECT regexp_replace(data, '^(.{60}).*(.{20})$', '\1..\2') AS shortened,
|
||||
length(data) AS len
|
||||
FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||
shortened | len
|
||||
------------------------------------------------------------------------------------+--------
|
||||
BEGIN | 5
|
||||
table public.toasted_oid8: INSERT: id[integer]:1 data[text]:..2345678901234567890' | 200062
|
||||
COMMIT | 6
|
||||
BEGIN | 5
|
||||
table public.toasted_oid8: INSERT: id[integer]:2 data[text]:..2345678901234567890' | 200062
|
||||
COMMIT | 6
|
||||
BEGIN | 5
|
||||
table public.toasted_oid8: UPDATE: old-key: id[integer]:1 ne..nchanged-toast-datum | 116
|
||||
COMMIT | 6
|
||||
BEGIN | 5
|
||||
table public.toasted_oid8: UPDATE: id[integer]:2 data[text]:..bcdefghijabcdefghij' | 200062
|
||||
COMMIT | 6
|
||||
BEGIN | 5
|
||||
table public.toasted_oid8: DELETE: id[integer]:11 | 49
|
||||
table public.toasted_oid8: DELETE: id[integer]:2 | 48
|
||||
COMMIT | 6
|
||||
(16 rows)
|
||||
|
||||
DROP TABLE toasted_oid8;
|
||||
SELECT pg_drop_replication_slot('regression_slot');
|
||||
pg_drop_replication_slot
|
||||
--------------------------
|
||||
|
||||
@@ -324,4 +324,24 @@ INSERT INTO tbl2 VALUES(1);
|
||||
commit;
|
||||
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||
|
||||
-- Test decoding of TOAST values with oid8
|
||||
CREATE TABLE toasted_oid8 (id serial primary key, data text)
|
||||
WITH (toast_value_type = 'oid8');
|
||||
-- uncompressed external toast data
|
||||
ALTER TABLE toasted_oid8 ALTER COLUMN data SET STORAGE EXTERNAL;
|
||||
INSERT INTO toasted_oid8(data) VALUES (repeat('1234567890', 20000));
|
||||
-- compressed external toast data
|
||||
ALTER TABLE toasted_oid8 ALTER COLUMN data SET STORAGE EXTENDED;
|
||||
INSERT INTO toasted_oid8(data) VALUES (repeat('1234567890', 20000));
|
||||
-- update without changing the toasted column, reported as unchanged
|
||||
UPDATE toasted_oid8 SET id = id + 10 WHERE id = 1;
|
||||
-- update changing the toasted column
|
||||
UPDATE toasted_oid8 SET data = repeat('abcdefghij', 20000) WHERE id = 2;
|
||||
DELETE FROM toasted_oid8;
|
||||
-- Check that the values are reassembled in full.
|
||||
SELECT regexp_replace(data, '^(.{60}).*(.{20})$', '\1..\2') AS shortened,
|
||||
length(data) AS len
|
||||
FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
|
||||
DROP TABLE toasted_oid8;
|
||||
|
||||
SELECT pg_drop_replication_slot('regression_slot');
|
||||
|
||||
@@ -66,6 +66,47 @@ SELECT SUBSTR(f1, 200, 5) FROM cmdata2;
|
||||
(1 row)
|
||||
|
||||
DROP TABLE cmdata2;
|
||||
-- pg_column_compression() with oid and oid8
|
||||
CREATE TABLE toastcomp_oid(f1 text) WITH (toast_value_type = 'oid');
|
||||
CREATE TABLE toastcomp_oid8(f1 text) WITH (toast_value_type = 'oid8');
|
||||
ALTER TABLE toastcomp_oid ALTER COLUMN f1 SET STORAGE EXTERNAL;
|
||||
ALTER TABLE toastcomp_oid8 ALTER COLUMN f1 SET STORAGE EXTERNAL;
|
||||
INSERT INTO toastcomp_oid VALUES (repeat('1234567890', 10000));
|
||||
INSERT INTO toastcomp_oid8 VALUES (repeat('1234567890', 10000));
|
||||
SELECT pg_column_compression(f1) IS NULL AS uncompressed FROM toastcomp_oid;
|
||||
uncompressed
|
||||
--------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT pg_column_compression(f1) IS NULL AS uncompressed FROM toastcomp_oid8;
|
||||
uncompressed
|
||||
--------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
-- out-of-line and compressed.
|
||||
TRUNCATE toastcomp_oid;
|
||||
TRUNCATE toastcomp_oid8;
|
||||
ALTER TABLE toastcomp_oid ALTER COLUMN f1 SET STORAGE EXTENDED;
|
||||
ALTER TABLE toastcomp_oid8 ALTER COLUMN f1 SET STORAGE EXTENDED;
|
||||
ALTER TABLE toastcomp_oid SET (toast_tuple_target = 128);
|
||||
ALTER TABLE toastcomp_oid8 SET (toast_tuple_target = 128);
|
||||
INSERT INTO toastcomp_oid VALUES (repeat('1234567890', 10000));
|
||||
INSERT INTO toastcomp_oid8 VALUES (repeat('1234567890', 10000));
|
||||
SELECT pg_column_compression(f1) FROM toastcomp_oid;
|
||||
pg_column_compression
|
||||
-----------------------
|
||||
pglz
|
||||
(1 row)
|
||||
|
||||
SELECT pg_column_compression(f1) FROM toastcomp_oid8;
|
||||
pg_column_compression
|
||||
-----------------------
|
||||
pglz
|
||||
(1 row)
|
||||
|
||||
DROP TABLE toastcomp_oid, toastcomp_oid8;
|
||||
--test column type update varlena/non-varlena
|
||||
CREATE TABLE cmdata2 (f1 int);
|
||||
\d+ cmdata2
|
||||
|
||||
@@ -1980,21 +1980,37 @@ SELECT text 'text' || varchar ' and varchar' AS "Concat text to varchar";
|
||||
(1 row)
|
||||
|
||||
--
|
||||
-- test substr with toasted text values
|
||||
-- Test substr with toasted text values, for all types of TOAST relations
|
||||
-- supported.
|
||||
--
|
||||
CREATE TABLE toasttest(f1 text);
|
||||
insert into toasttest values(repeat('1234567890',10000));
|
||||
insert into toasttest values(repeat('1234567890',10000));
|
||||
CREATE TABLE toasttest_oid(f1 text) with (toast_value_type = 'oid');
|
||||
CREATE TABLE toasttest_oid8(f1 text) with (toast_value_type = 'oid8');
|
||||
insert into toasttest_oid values(repeat('1234567890',10000));
|
||||
insert into toasttest_oid values(repeat('1234567890',10000));
|
||||
insert into toasttest_oid8 values(repeat('1234567890',10000));
|
||||
insert into toasttest_oid8 values(repeat('1234567890',10000));
|
||||
--
|
||||
-- Ensure that some values are uncompressed, to test the faster substring
|
||||
-- operation used in that case
|
||||
--
|
||||
alter table toasttest alter column f1 set storage external;
|
||||
insert into toasttest values(repeat('1234567890',10000));
|
||||
insert into toasttest values(repeat('1234567890',10000));
|
||||
alter table toasttest_oid alter column f1 set storage external;
|
||||
insert into toasttest_oid values(repeat('1234567890',10000));
|
||||
insert into toasttest_oid values(repeat('1234567890',10000));
|
||||
alter table toasttest_oid8 alter column f1 set storage external;
|
||||
insert into toasttest_oid8 values(repeat('1234567890',10000));
|
||||
insert into toasttest_oid8 values(repeat('1234567890',10000));
|
||||
-- If the starting position is zero or less, then return from the start of the string
|
||||
-- adjusting the length to be consistent with the "negative start" per SQL.
|
||||
SELECT substr(f1, -1, 5) from toasttest;
|
||||
SELECT substr(f1, -1, 5) from toasttest_oid;
|
||||
substr
|
||||
--------
|
||||
123
|
||||
123
|
||||
123
|
||||
123
|
||||
(4 rows)
|
||||
|
||||
SELECT substr(f1, -1, 5) from toasttest_oid8;
|
||||
substr
|
||||
--------
|
||||
123
|
||||
@@ -2004,11 +2020,22 @@ SELECT substr(f1, -1, 5) from toasttest;
|
||||
(4 rows)
|
||||
|
||||
-- If the length is less than zero, an ERROR is thrown.
|
||||
SELECT substr(f1, 5, -1) from toasttest;
|
||||
SELECT substr(f1, 5, -1) from toasttest_oid;
|
||||
ERROR: negative substring length not allowed
|
||||
SELECT substr(f1, 5, -1) from toasttest_oid8;
|
||||
ERROR: negative substring length not allowed
|
||||
-- If no third argument (length) is provided, the length to the end of the
|
||||
-- string is assumed.
|
||||
SELECT substr(f1, 99995) from toasttest;
|
||||
SELECT substr(f1, 99995) from toasttest_oid;
|
||||
substr
|
||||
--------
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
(4 rows)
|
||||
|
||||
SELECT substr(f1, 99995) from toasttest_oid8;
|
||||
substr
|
||||
--------
|
||||
567890
|
||||
@@ -2019,7 +2046,7 @@ SELECT substr(f1, 99995) from toasttest;
|
||||
|
||||
-- If start plus length is > string length, the result is truncated to
|
||||
-- string length
|
||||
SELECT substr(f1, 99995, 10) from toasttest;
|
||||
SELECT substr(f1, 99995, 10) from toasttest_oid;
|
||||
substr
|
||||
--------
|
||||
567890
|
||||
@@ -2028,50 +2055,105 @@ SELECT substr(f1, 99995, 10) from toasttest;
|
||||
567890
|
||||
(4 rows)
|
||||
|
||||
TRUNCATE TABLE toasttest;
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
SELECT substr(f1, 99995, 10) from toasttest_oid8;
|
||||
substr
|
||||
--------
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
(4 rows)
|
||||
|
||||
-- TRUNCATE cases for TOAST relations with OID values.
|
||||
TRUNCATE TABLE toasttest_oid;
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
-- expect >0 blocks
|
||||
SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
|
||||
FROM pg_class where relname = 'toasttest';
|
||||
FROM pg_class where relname = 'toasttest_oid';
|
||||
is_empty
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
TRUNCATE TABLE toasttest;
|
||||
ALTER TABLE toasttest set (toast_tuple_target = 4080);
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
TRUNCATE TABLE toasttest_oid;
|
||||
ALTER TABLE toasttest_oid set (toast_tuple_target = 4080);
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
-- expect 0 blocks
|
||||
SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
|
||||
FROM pg_class where relname = 'toasttest';
|
||||
FROM pg_class where relname = 'toasttest_oid';
|
||||
is_empty
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
DROP TABLE toasttest;
|
||||
DROP TABLE toasttest_oid;
|
||||
-- TRUNCATE cases for TOAST relation with int8 values.
|
||||
TRUNCATE TABLE toasttest_oid8;
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
-- expect >0 blocks
|
||||
SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
|
||||
FROM pg_class where relname = 'toasttest_oid8';
|
||||
is_empty
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
TRUNCATE TABLE toasttest_oid8;
|
||||
ALTER TABLE toasttest_oid8 set (toast_tuple_target = 4080);
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
-- expect 0 blocks
|
||||
SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
|
||||
FROM pg_class where relname = 'toasttest_oid8';
|
||||
is_empty
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
DROP TABLE toasttest_oid8;
|
||||
--
|
||||
-- test substr with toasted bytea values
|
||||
-- test substr with toasted bytea values, for all types of TOAST relations
|
||||
-- supported. Do not drop these two relations, for pg_upgrade.
|
||||
--
|
||||
CREATE TABLE toasttest(f1 bytea);
|
||||
insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
|
||||
CREATE TABLE toasttest_oid(f1 bytea) WITH (toast_value_type = 'oid');
|
||||
CREATE TABLE toasttest_oid8(f1 bytea) WITH (toast_value_type = 'oid8');
|
||||
insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest_oid8 values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest_oid8 values(decode(repeat('1234567890',10000),'escape'));
|
||||
--
|
||||
-- Ensure that some values are uncompressed, to test the faster substring
|
||||
-- operation used in that case
|
||||
--
|
||||
alter table toasttest alter column f1 set storage external;
|
||||
insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
|
||||
alter table toasttest_oid alter column f1 set storage external;
|
||||
insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
|
||||
alter table toasttest_oid8 alter column f1 set storage external;
|
||||
insert into toasttest_oid8 values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest_oid8 values(decode(repeat('1234567890',10000),'escape'));
|
||||
-- If the starting position is zero or less, then return from the start of the string
|
||||
-- adjusting the length to be consistent with the "negative start" per SQL.
|
||||
SELECT substr(f1, -1, 5) from toasttest;
|
||||
SELECT substr(f1, -1, 5) from toasttest_oid;
|
||||
substr
|
||||
--------
|
||||
123
|
||||
123
|
||||
123
|
||||
123
|
||||
(4 rows)
|
||||
|
||||
SELECT substr(f1, -1, 5) from toasttest_oid8;
|
||||
substr
|
||||
--------
|
||||
123
|
||||
@@ -2081,11 +2163,22 @@ SELECT substr(f1, -1, 5) from toasttest;
|
||||
(4 rows)
|
||||
|
||||
-- If the length is less than zero, an ERROR is thrown.
|
||||
SELECT substr(f1, 5, -1) from toasttest;
|
||||
SELECT substr(f1, 5, -1) from toasttest_oid;
|
||||
ERROR: negative substring length not allowed
|
||||
SELECT substr(f1, 5, -1) from toasttest_oid8;
|
||||
ERROR: negative substring length not allowed
|
||||
-- If no third argument (length) is provided, the length to the end of the
|
||||
-- string is assumed.
|
||||
SELECT substr(f1, 99995) from toasttest;
|
||||
SELECT substr(f1, 99995) from toasttest_oid;
|
||||
substr
|
||||
--------
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
(4 rows)
|
||||
|
||||
SELECT substr(f1, 99995) from toasttest_oid8;
|
||||
substr
|
||||
--------
|
||||
567890
|
||||
@@ -2096,7 +2189,7 @@ SELECT substr(f1, 99995) from toasttest;
|
||||
|
||||
-- If start plus length is > string length, the result is truncated to
|
||||
-- string length
|
||||
SELECT substr(f1, 99995, 10) from toasttest;
|
||||
SELECT substr(f1, 99995, 10) from toasttest_oid;
|
||||
substr
|
||||
--------
|
||||
567890
|
||||
@@ -2105,7 +2198,234 @@ SELECT substr(f1, 99995, 10) from toasttest;
|
||||
567890
|
||||
(4 rows)
|
||||
|
||||
DROP TABLE toasttest;
|
||||
SELECT substr(f1, 99995, 10) from toasttest_oid8;
|
||||
substr
|
||||
--------
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
(4 rows)
|
||||
|
||||
-- A relation rewrite leaves the TOAST value attributes unchanged.
|
||||
VACUUM FULL toasttest_oid;
|
||||
VACUUM FULL toasttest_oid8;
|
||||
SELECT c1.relname, a.atttypid::regtype
|
||||
FROM pg_attribute AS a,
|
||||
pg_class AS c1,
|
||||
pg_class AS c2
|
||||
WHERE
|
||||
c1.relname IN ('toasttest_oid', 'toasttest_oid8') AND
|
||||
c1.reltoastrelid = c2.oid AND
|
||||
a.attrelid = c2.oid AND
|
||||
a.attname = 'chunk_id'
|
||||
ORDER BY c1.relname COLLATE "C";
|
||||
relname | atttypid
|
||||
----------------+----------
|
||||
toasttest_oid | oid
|
||||
toasttest_oid8 | oid8
|
||||
(2 rows)
|
||||
|
||||
-- Check that data slices are still accessible.
|
||||
SELECT substr(f1, 99995) from toasttest_oid;
|
||||
substr
|
||||
--------
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
(4 rows)
|
||||
|
||||
SELECT substr(f1, 99995) from toasttest_oid8;
|
||||
substr
|
||||
--------
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
(4 rows)
|
||||
|
||||
SELECT substr(f1, 99995, 10) from toasttest_oid;
|
||||
substr
|
||||
--------
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
(4 rows)
|
||||
|
||||
SELECT substr(f1, 99995, 10) from toasttest_oid8;
|
||||
substr
|
||||
--------
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
567890
|
||||
(4 rows)
|
||||
|
||||
-- ALTER TABLE after TOAST table creation does not affect relation rewrite.
|
||||
ALTER TABLE toasttest_oid SET (toast_value_type = oid8);
|
||||
ALTER TABLE toasttest_oid8 SET (toast_value_type = oid);
|
||||
VACUUM FULL toasttest_oid;
|
||||
VACUUM FULL toasttest_oid8;
|
||||
SELECT c1.relname, a.atttypid::regtype
|
||||
FROM pg_attribute AS a,
|
||||
pg_class AS c1,
|
||||
pg_class AS c2
|
||||
WHERE
|
||||
c1.relname IN ('toasttest_oid', 'toasttest_oid8') AND
|
||||
c1.reltoastrelid = c2.oid AND
|
||||
a.attrelid = c2.oid AND
|
||||
a.attname = 'chunk_id'
|
||||
ORDER BY c1.relname COLLATE "C";
|
||||
relname | atttypid
|
||||
----------------+----------
|
||||
toasttest_oid | oid
|
||||
toasttest_oid8 | oid8
|
||||
(2 rows)
|
||||
|
||||
REPACK toasttest_oid;
|
||||
REPACK toasttest_oid8;
|
||||
SELECT c1.relname, a.atttypid::regtype
|
||||
FROM pg_attribute AS a,
|
||||
pg_class AS c1,
|
||||
pg_class AS c2
|
||||
WHERE
|
||||
c1.relname IN ('toasttest_oid', 'toasttest_oid8') AND
|
||||
c1.reltoastrelid = c2.oid AND
|
||||
a.attrelid = c2.oid AND
|
||||
a.attname = 'chunk_id'
|
||||
ORDER BY c1.relname COLLATE "C";
|
||||
relname | atttypid
|
||||
----------------+----------
|
||||
toasttest_oid | oid
|
||||
toasttest_oid8 | oid8
|
||||
(2 rows)
|
||||
|
||||
ALTER TABLE toasttest_oid RESET (toast_value_type);
|
||||
ALTER TABLE toasttest_oid8 RESET (toast_value_type);
|
||||
-- Reset column storage to its default
|
||||
ALTER TABLE toasttest_oid ALTER COLUMN f1 SET STORAGE EXTENDED;
|
||||
ALTER TABLE toasttest_oid8 ALTER COLUMN f1 SET STORAGE EXTENDED;
|
||||
-- UPDATE with out-of-line datum that belongs to another TOAST table.
|
||||
CREATE TABLE toastupd_oid(f1 text) WITH (toast_value_type = 'oid');
|
||||
CREATE TABLE toastupd_oid8(f1 text) WITH (toast_value_type = 'oid8');
|
||||
ALTER TABLE toastupd_oid ALTER COLUMN f1 SET STORAGE EXTERNAL;
|
||||
ALTER TABLE toastupd_oid8 ALTER COLUMN f1 SET STORAGE EXTERNAL;
|
||||
SELECT reltoastrelid::regclass AS upd_oid_toast FROM pg_class
|
||||
WHERE oid = 'toastupd_oid'::regclass \gset
|
||||
SELECT reltoastrelid::regclass AS upd_oid8_toast FROM pg_class
|
||||
WHERE oid = 'toastupd_oid8'::regclass \gset
|
||||
INSERT INTO toastupd_oid VALUES (repeat('a', 100000));
|
||||
INSERT INTO toastupd_oid8 VALUES (repeat('b', 100000));
|
||||
-- old value is an oid8 pointer, new value an oid pointer.
|
||||
UPDATE toastupd_oid8 SET f1 = toastupd_oid.f1 FROM toastupd_oid;
|
||||
SELECT length(f1), substr(f1, 1, 3) FROM toastupd_oid8;
|
||||
length | substr
|
||||
--------+--------
|
||||
100000 | aaa
|
||||
(1 row)
|
||||
|
||||
-- replaced value must be gone, leaving a single value behind.
|
||||
SELECT count(*) FROM :upd_oid8_toast WHERE chunk_seq = 0;
|
||||
count
|
||||
-------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
-- reverse: old value is an oid pointer, new value an oid8 pointer.
|
||||
TRUNCATE toastupd_oid;
|
||||
INSERT INTO toastupd_oid VALUES (repeat('c', 100000));
|
||||
UPDATE toastupd_oid SET f1 = toastupd_oid8.f1 FROM toastupd_oid8;
|
||||
SELECT length(f1), substr(f1, 1, 3) FROM toastupd_oid;
|
||||
length | substr
|
||||
--------+--------
|
||||
100000 | aaa
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM :upd_oid_toast WHERE chunk_seq = 0;
|
||||
count
|
||||
-------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
DROP TABLE toastupd_oid, toastupd_oid8;
|
||||
-- TOAST table manipulations with ALTER TABLE and attributes
|
||||
CREATE TABLE toast_alter_oid (f1 int, f2 text STORAGE EXTERNAL)
|
||||
WITH (toast_value_type = 'oid');
|
||||
INSERT INTO toast_alter_oid VALUES (1, repeat('1234567890',10000));
|
||||
INSERT INTO toast_alter_oid VALUES (2, repeat('2234567890',10000));
|
||||
BEGIN;
|
||||
ALTER TABLE toast_alter_oid DROP COLUMN f2;
|
||||
SELECT reltoastrelid::regclass > 0 AS has_toast FROM pg_class
|
||||
WHERE oid = 'toast_alter_oid'::regclass;
|
||||
has_toast
|
||||
-----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
-- New toast_value_type has no effect.
|
||||
ALTER TABLE toast_alter_oid SET (toast_value_type = 'oid8');
|
||||
ALTER TABLE toast_alter_oid ADD COLUMN f3 text
|
||||
DEFAULT (repeat('5234567890',10000));
|
||||
ALTER TABLE toast_alter_oid ALTER COLUMN f3 SET STORAGE EXTERNAL;
|
||||
INSERT INTO toast_alter_oid VALUES (3, repeat('3234567890',10000));
|
||||
INSERT INTO toast_alter_oid VALUES (4, repeat('4234567890',10000));
|
||||
SELECT reltoastrelid::regclass > 0 AS has_toast FROM pg_class
|
||||
WHERE oid = 'toast_alter_oid'::regclass;
|
||||
has_toast
|
||||
-----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT c1.relname, a.atttypid::regtype
|
||||
FROM pg_attribute AS a,
|
||||
pg_class AS c1,
|
||||
pg_class AS c2
|
||||
WHERE
|
||||
c1.relname = 'toast_alter_oid' AND
|
||||
c1.reltoastrelid = c2.oid AND
|
||||
a.attrelid = c2.oid AND
|
||||
a.attname = 'chunk_id'
|
||||
ORDER BY c1.relname COLLATE "C";
|
||||
relname | atttypid
|
||||
-----------------+----------
|
||||
toast_alter_oid | oid
|
||||
(1 row)
|
||||
|
||||
SELECT f1, substr(f3, 99991) FROM toast_alter_oid;
|
||||
f1 | substr
|
||||
----+------------
|
||||
1 | 5234567890
|
||||
2 | 5234567890
|
||||
3 | 3234567890
|
||||
4 | 4234567890
|
||||
(4 rows)
|
||||
|
||||
ROLLBACK;
|
||||
SELECT f1, substr(f2, 99991) FROM toast_alter_oid;
|
||||
f1 | substr
|
||||
----+------------
|
||||
1 | 1234567890
|
||||
2 | 2234567890
|
||||
(2 rows)
|
||||
|
||||
SELECT c1.relname, a.atttypid::regtype
|
||||
FROM pg_attribute AS a,
|
||||
pg_class AS c1,
|
||||
pg_class AS c2
|
||||
WHERE
|
||||
c1.relname = 'toast_alter_oid' AND
|
||||
c1.reltoastrelid = c2.oid AND
|
||||
a.attrelid = c2.oid AND
|
||||
a.attname = 'chunk_id'
|
||||
ORDER BY c1.relname COLLATE "C";
|
||||
relname | atttypid
|
||||
-----------------+----------
|
||||
toast_alter_oid | oid
|
||||
(1 row)
|
||||
|
||||
DROP TABLE toast_alter_oid;
|
||||
-- test internally compressing datums
|
||||
-- this tests compressing a datum to a very small size which exercises a
|
||||
-- corner case in packed-varlena handling: even though small, the compressed
|
||||
|
||||
@@ -37,6 +37,28 @@ SELECT pg_column_compression(f1) FROM cmdata2;
|
||||
SELECT SUBSTR(f1, 200, 5) FROM cmdata2;
|
||||
DROP TABLE cmdata2;
|
||||
|
||||
-- pg_column_compression() with oid and oid8
|
||||
CREATE TABLE toastcomp_oid(f1 text) WITH (toast_value_type = 'oid');
|
||||
CREATE TABLE toastcomp_oid8(f1 text) WITH (toast_value_type = 'oid8');
|
||||
ALTER TABLE toastcomp_oid ALTER COLUMN f1 SET STORAGE EXTERNAL;
|
||||
ALTER TABLE toastcomp_oid8 ALTER COLUMN f1 SET STORAGE EXTERNAL;
|
||||
INSERT INTO toastcomp_oid VALUES (repeat('1234567890', 10000));
|
||||
INSERT INTO toastcomp_oid8 VALUES (repeat('1234567890', 10000));
|
||||
SELECT pg_column_compression(f1) IS NULL AS uncompressed FROM toastcomp_oid;
|
||||
SELECT pg_column_compression(f1) IS NULL AS uncompressed FROM toastcomp_oid8;
|
||||
-- out-of-line and compressed.
|
||||
TRUNCATE toastcomp_oid;
|
||||
TRUNCATE toastcomp_oid8;
|
||||
ALTER TABLE toastcomp_oid ALTER COLUMN f1 SET STORAGE EXTENDED;
|
||||
ALTER TABLE toastcomp_oid8 ALTER COLUMN f1 SET STORAGE EXTENDED;
|
||||
ALTER TABLE toastcomp_oid SET (toast_tuple_target = 128);
|
||||
ALTER TABLE toastcomp_oid8 SET (toast_tuple_target = 128);
|
||||
INSERT INTO toastcomp_oid VALUES (repeat('1234567890', 10000));
|
||||
INSERT INTO toastcomp_oid8 VALUES (repeat('1234567890', 10000));
|
||||
SELECT pg_column_compression(f1) FROM toastcomp_oid;
|
||||
SELECT pg_column_compression(f1) FROM toastcomp_oid8;
|
||||
DROP TABLE toastcomp_oid, toastcomp_oid8;
|
||||
|
||||
--test column type update varlena/non-varlena
|
||||
CREATE TABLE cmdata2 (f1 int);
|
||||
\d+ cmdata2
|
||||
|
||||
@@ -563,89 +563,248 @@ SELECT text 'text' || char(20) ' and characters' AS "Concat text to char";
|
||||
SELECT text 'text' || varchar ' and varchar' AS "Concat text to varchar";
|
||||
|
||||
--
|
||||
-- test substr with toasted text values
|
||||
-- Test substr with toasted text values, for all types of TOAST relations
|
||||
-- supported.
|
||||
--
|
||||
CREATE TABLE toasttest(f1 text);
|
||||
CREATE TABLE toasttest_oid(f1 text) with (toast_value_type = 'oid');
|
||||
CREATE TABLE toasttest_oid8(f1 text) with (toast_value_type = 'oid8');
|
||||
|
||||
insert into toasttest values(repeat('1234567890',10000));
|
||||
insert into toasttest values(repeat('1234567890',10000));
|
||||
insert into toasttest_oid values(repeat('1234567890',10000));
|
||||
insert into toasttest_oid values(repeat('1234567890',10000));
|
||||
insert into toasttest_oid8 values(repeat('1234567890',10000));
|
||||
insert into toasttest_oid8 values(repeat('1234567890',10000));
|
||||
|
||||
--
|
||||
-- Ensure that some values are uncompressed, to test the faster substring
|
||||
-- operation used in that case
|
||||
--
|
||||
alter table toasttest alter column f1 set storage external;
|
||||
insert into toasttest values(repeat('1234567890',10000));
|
||||
insert into toasttest values(repeat('1234567890',10000));
|
||||
alter table toasttest_oid alter column f1 set storage external;
|
||||
insert into toasttest_oid values(repeat('1234567890',10000));
|
||||
insert into toasttest_oid values(repeat('1234567890',10000));
|
||||
alter table toasttest_oid8 alter column f1 set storage external;
|
||||
insert into toasttest_oid8 values(repeat('1234567890',10000));
|
||||
insert into toasttest_oid8 values(repeat('1234567890',10000));
|
||||
|
||||
-- If the starting position is zero or less, then return from the start of the string
|
||||
-- adjusting the length to be consistent with the "negative start" per SQL.
|
||||
SELECT substr(f1, -1, 5) from toasttest;
|
||||
SELECT substr(f1, -1, 5) from toasttest_oid;
|
||||
SELECT substr(f1, -1, 5) from toasttest_oid8;
|
||||
|
||||
-- If the length is less than zero, an ERROR is thrown.
|
||||
SELECT substr(f1, 5, -1) from toasttest;
|
||||
SELECT substr(f1, 5, -1) from toasttest_oid;
|
||||
SELECT substr(f1, 5, -1) from toasttest_oid8;
|
||||
|
||||
-- If no third argument (length) is provided, the length to the end of the
|
||||
-- string is assumed.
|
||||
SELECT substr(f1, 99995) from toasttest;
|
||||
SELECT substr(f1, 99995) from toasttest_oid;
|
||||
SELECT substr(f1, 99995) from toasttest_oid8;
|
||||
|
||||
-- If start plus length is > string length, the result is truncated to
|
||||
-- string length
|
||||
SELECT substr(f1, 99995, 10) from toasttest;
|
||||
SELECT substr(f1, 99995, 10) from toasttest_oid;
|
||||
SELECT substr(f1, 99995, 10) from toasttest_oid8;
|
||||
|
||||
TRUNCATE TABLE toasttest;
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
-- TRUNCATE cases for TOAST relations with OID values.
|
||||
TRUNCATE TABLE toasttest_oid;
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
-- expect >0 blocks
|
||||
SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
|
||||
FROM pg_class where relname = 'toasttest';
|
||||
|
||||
TRUNCATE TABLE toasttest;
|
||||
ALTER TABLE toasttest set (toast_tuple_target = 4080);
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest values (repeat('1234567890',300));
|
||||
FROM pg_class where relname = 'toasttest_oid';
|
||||
TRUNCATE TABLE toasttest_oid;
|
||||
ALTER TABLE toasttest_oid set (toast_tuple_target = 4080);
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid values (repeat('1234567890',300));
|
||||
-- expect 0 blocks
|
||||
SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
|
||||
FROM pg_class where relname = 'toasttest';
|
||||
FROM pg_class where relname = 'toasttest_oid';
|
||||
DROP TABLE toasttest_oid;
|
||||
|
||||
DROP TABLE toasttest;
|
||||
-- TRUNCATE cases for TOAST relation with int8 values.
|
||||
TRUNCATE TABLE toasttest_oid8;
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
-- expect >0 blocks
|
||||
SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
|
||||
FROM pg_class where relname = 'toasttest_oid8';
|
||||
TRUNCATE TABLE toasttest_oid8;
|
||||
ALTER TABLE toasttest_oid8 set (toast_tuple_target = 4080);
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
INSERT INTO toasttest_oid8 values (repeat('1234567890',300));
|
||||
-- expect 0 blocks
|
||||
SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
|
||||
FROM pg_class where relname = 'toasttest_oid8';
|
||||
DROP TABLE toasttest_oid8;
|
||||
|
||||
--
|
||||
-- test substr with toasted bytea values
|
||||
-- test substr with toasted bytea values, for all types of TOAST relations
|
||||
-- supported. Do not drop these two relations, for pg_upgrade.
|
||||
--
|
||||
CREATE TABLE toasttest(f1 bytea);
|
||||
CREATE TABLE toasttest_oid(f1 bytea) WITH (toast_value_type = 'oid');
|
||||
CREATE TABLE toasttest_oid8(f1 bytea) WITH (toast_value_type = 'oid8');
|
||||
|
||||
insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest_oid8 values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest_oid8 values(decode(repeat('1234567890',10000),'escape'));
|
||||
|
||||
--
|
||||
-- Ensure that some values are uncompressed, to test the faster substring
|
||||
-- operation used in that case
|
||||
--
|
||||
alter table toasttest alter column f1 set storage external;
|
||||
insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
|
||||
alter table toasttest_oid alter column f1 set storage external;
|
||||
insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
|
||||
alter table toasttest_oid8 alter column f1 set storage external;
|
||||
insert into toasttest_oid8 values(decode(repeat('1234567890',10000),'escape'));
|
||||
insert into toasttest_oid8 values(decode(repeat('1234567890',10000),'escape'));
|
||||
|
||||
-- If the starting position is zero or less, then return from the start of the string
|
||||
-- adjusting the length to be consistent with the "negative start" per SQL.
|
||||
SELECT substr(f1, -1, 5) from toasttest;
|
||||
SELECT substr(f1, -1, 5) from toasttest_oid;
|
||||
SELECT substr(f1, -1, 5) from toasttest_oid8;
|
||||
|
||||
-- If the length is less than zero, an ERROR is thrown.
|
||||
SELECT substr(f1, 5, -1) from toasttest;
|
||||
SELECT substr(f1, 5, -1) from toasttest_oid;
|
||||
SELECT substr(f1, 5, -1) from toasttest_oid8;
|
||||
|
||||
-- If no third argument (length) is provided, the length to the end of the
|
||||
-- string is assumed.
|
||||
SELECT substr(f1, 99995) from toasttest;
|
||||
SELECT substr(f1, 99995) from toasttest_oid;
|
||||
SELECT substr(f1, 99995) from toasttest_oid8;
|
||||
|
||||
-- If start plus length is > string length, the result is truncated to
|
||||
-- string length
|
||||
SELECT substr(f1, 99995, 10) from toasttest;
|
||||
SELECT substr(f1, 99995, 10) from toasttest_oid;
|
||||
SELECT substr(f1, 99995, 10) from toasttest_oid8;
|
||||
|
||||
DROP TABLE toasttest;
|
||||
-- A relation rewrite leaves the TOAST value attributes unchanged.
|
||||
VACUUM FULL toasttest_oid;
|
||||
VACUUM FULL toasttest_oid8;
|
||||
SELECT c1.relname, a.atttypid::regtype
|
||||
FROM pg_attribute AS a,
|
||||
pg_class AS c1,
|
||||
pg_class AS c2
|
||||
WHERE
|
||||
c1.relname IN ('toasttest_oid', 'toasttest_oid8') AND
|
||||
c1.reltoastrelid = c2.oid AND
|
||||
a.attrelid = c2.oid AND
|
||||
a.attname = 'chunk_id'
|
||||
ORDER BY c1.relname COLLATE "C";
|
||||
-- Check that data slices are still accessible.
|
||||
SELECT substr(f1, 99995) from toasttest_oid;
|
||||
SELECT substr(f1, 99995) from toasttest_oid8;
|
||||
SELECT substr(f1, 99995, 10) from toasttest_oid;
|
||||
SELECT substr(f1, 99995, 10) from toasttest_oid8;
|
||||
|
||||
-- ALTER TABLE after TOAST table creation does not affect relation rewrite.
|
||||
ALTER TABLE toasttest_oid SET (toast_value_type = oid8);
|
||||
ALTER TABLE toasttest_oid8 SET (toast_value_type = oid);
|
||||
VACUUM FULL toasttest_oid;
|
||||
VACUUM FULL toasttest_oid8;
|
||||
SELECT c1.relname, a.atttypid::regtype
|
||||
FROM pg_attribute AS a,
|
||||
pg_class AS c1,
|
||||
pg_class AS c2
|
||||
WHERE
|
||||
c1.relname IN ('toasttest_oid', 'toasttest_oid8') AND
|
||||
c1.reltoastrelid = c2.oid AND
|
||||
a.attrelid = c2.oid AND
|
||||
a.attname = 'chunk_id'
|
||||
ORDER BY c1.relname COLLATE "C";
|
||||
REPACK toasttest_oid;
|
||||
REPACK toasttest_oid8;
|
||||
SELECT c1.relname, a.atttypid::regtype
|
||||
FROM pg_attribute AS a,
|
||||
pg_class AS c1,
|
||||
pg_class AS c2
|
||||
WHERE
|
||||
c1.relname IN ('toasttest_oid', 'toasttest_oid8') AND
|
||||
c1.reltoastrelid = c2.oid AND
|
||||
a.attrelid = c2.oid AND
|
||||
a.attname = 'chunk_id'
|
||||
ORDER BY c1.relname COLLATE "C";
|
||||
ALTER TABLE toasttest_oid RESET (toast_value_type);
|
||||
ALTER TABLE toasttest_oid8 RESET (toast_value_type);
|
||||
|
||||
-- Reset column storage to its default
|
||||
ALTER TABLE toasttest_oid ALTER COLUMN f1 SET STORAGE EXTENDED;
|
||||
ALTER TABLE toasttest_oid8 ALTER COLUMN f1 SET STORAGE EXTENDED;
|
||||
|
||||
-- UPDATE with out-of-line datum that belongs to another TOAST table.
|
||||
CREATE TABLE toastupd_oid(f1 text) WITH (toast_value_type = 'oid');
|
||||
CREATE TABLE toastupd_oid8(f1 text) WITH (toast_value_type = 'oid8');
|
||||
ALTER TABLE toastupd_oid ALTER COLUMN f1 SET STORAGE EXTERNAL;
|
||||
ALTER TABLE toastupd_oid8 ALTER COLUMN f1 SET STORAGE EXTERNAL;
|
||||
SELECT reltoastrelid::regclass AS upd_oid_toast FROM pg_class
|
||||
WHERE oid = 'toastupd_oid'::regclass \gset
|
||||
SELECT reltoastrelid::regclass AS upd_oid8_toast FROM pg_class
|
||||
WHERE oid = 'toastupd_oid8'::regclass \gset
|
||||
INSERT INTO toastupd_oid VALUES (repeat('a', 100000));
|
||||
INSERT INTO toastupd_oid8 VALUES (repeat('b', 100000));
|
||||
-- old value is an oid8 pointer, new value an oid pointer.
|
||||
UPDATE toastupd_oid8 SET f1 = toastupd_oid.f1 FROM toastupd_oid;
|
||||
SELECT length(f1), substr(f1, 1, 3) FROM toastupd_oid8;
|
||||
-- replaced value must be gone, leaving a single value behind.
|
||||
SELECT count(*) FROM :upd_oid8_toast WHERE chunk_seq = 0;
|
||||
-- reverse: old value is an oid pointer, new value an oid8 pointer.
|
||||
TRUNCATE toastupd_oid;
|
||||
INSERT INTO toastupd_oid VALUES (repeat('c', 100000));
|
||||
UPDATE toastupd_oid SET f1 = toastupd_oid8.f1 FROM toastupd_oid8;
|
||||
SELECT length(f1), substr(f1, 1, 3) FROM toastupd_oid;
|
||||
SELECT count(*) FROM :upd_oid_toast WHERE chunk_seq = 0;
|
||||
DROP TABLE toastupd_oid, toastupd_oid8;
|
||||
|
||||
-- TOAST table manipulations with ALTER TABLE and attributes
|
||||
CREATE TABLE toast_alter_oid (f1 int, f2 text STORAGE EXTERNAL)
|
||||
WITH (toast_value_type = 'oid');
|
||||
INSERT INTO toast_alter_oid VALUES (1, repeat('1234567890',10000));
|
||||
INSERT INTO toast_alter_oid VALUES (2, repeat('2234567890',10000));
|
||||
BEGIN;
|
||||
ALTER TABLE toast_alter_oid DROP COLUMN f2;
|
||||
SELECT reltoastrelid::regclass > 0 AS has_toast FROM pg_class
|
||||
WHERE oid = 'toast_alter_oid'::regclass;
|
||||
-- New toast_value_type has no effect.
|
||||
ALTER TABLE toast_alter_oid SET (toast_value_type = 'oid8');
|
||||
ALTER TABLE toast_alter_oid ADD COLUMN f3 text
|
||||
DEFAULT (repeat('5234567890',10000));
|
||||
ALTER TABLE toast_alter_oid ALTER COLUMN f3 SET STORAGE EXTERNAL;
|
||||
INSERT INTO toast_alter_oid VALUES (3, repeat('3234567890',10000));
|
||||
INSERT INTO toast_alter_oid VALUES (4, repeat('4234567890',10000));
|
||||
SELECT reltoastrelid::regclass > 0 AS has_toast FROM pg_class
|
||||
WHERE oid = 'toast_alter_oid'::regclass;
|
||||
SELECT c1.relname, a.atttypid::regtype
|
||||
FROM pg_attribute AS a,
|
||||
pg_class AS c1,
|
||||
pg_class AS c2
|
||||
WHERE
|
||||
c1.relname = 'toast_alter_oid' AND
|
||||
c1.reltoastrelid = c2.oid AND
|
||||
a.attrelid = c2.oid AND
|
||||
a.attname = 'chunk_id'
|
||||
ORDER BY c1.relname COLLATE "C";
|
||||
SELECT f1, substr(f3, 99991) FROM toast_alter_oid;
|
||||
ROLLBACK;
|
||||
SELECT f1, substr(f2, 99991) FROM toast_alter_oid;
|
||||
SELECT c1.relname, a.atttypid::regtype
|
||||
FROM pg_attribute AS a,
|
||||
pg_class AS c1,
|
||||
pg_class AS c2
|
||||
WHERE
|
||||
c1.relname = 'toast_alter_oid' AND
|
||||
c1.reltoastrelid = c2.oid AND
|
||||
a.attrelid = c2.oid AND
|
||||
a.attname = 'chunk_id'
|
||||
ORDER BY c1.relname COLLATE "C";
|
||||
DROP TABLE toast_alter_oid;
|
||||
|
||||
-- test internally compressing datums
|
||||
|
||||
|
||||
Reference in New Issue
Block a user