2002-08-10 22:43:46 +02:00
--
-- CLUSTER
--
CREATE TABLE clstr_tst_s (rf_a SERIAL PRIMARY KEY,
b INT);
CREATE TABLE clstr_tst (a SERIAL PRIMARY KEY,
b INT,
c TEXT,
2002-08-11 23:17:35 +02:00
d TEXT,
2002-08-10 22:43:46 +02:00
CONSTRAINT clstr_tst_con FOREIGN KEY (b) REFERENCES clstr_tst_s);
CREATE INDEX clstr_tst_b ON clstr_tst (b);
CREATE INDEX clstr_tst_c ON clstr_tst (c);
CREATE INDEX clstr_tst_c_b ON clstr_tst (c,b);
CREATE INDEX clstr_tst_b_c ON clstr_tst (b,c);
INSERT INTO clstr_tst_s (b) VALUES (0);
INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s;
INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s;
INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s;
INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s;
INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s;
CREATE TABLE clstr_tst_inh () INHERITS (clstr_tst);
INSERT INTO clstr_tst (b, c) VALUES (11, 'once');
INSERT INTO clstr_tst (b, c) VALUES (10, 'diez');
INSERT INTO clstr_tst (b, c) VALUES (31, 'treinta y uno');
INSERT INTO clstr_tst (b, c) VALUES (22, 'veintidos');
INSERT INTO clstr_tst (b, c) VALUES (3, 'tres');
INSERT INTO clstr_tst (b, c) VALUES (20, 'veinte');
INSERT INTO clstr_tst (b, c) VALUES (23, 'veintitres');
INSERT INTO clstr_tst (b, c) VALUES (21, 'veintiuno');
INSERT INTO clstr_tst (b, c) VALUES (4, 'cuatro');
INSERT INTO clstr_tst (b, c) VALUES (14, 'catorce');
INSERT INTO clstr_tst (b, c) VALUES (2, 'dos');
INSERT INTO clstr_tst (b, c) VALUES (18, 'dieciocho');
INSERT INTO clstr_tst (b, c) VALUES (27, 'veintisiete');
INSERT INTO clstr_tst (b, c) VALUES (25, 'veinticinco');
INSERT INTO clstr_tst (b, c) VALUES (13, 'trece');
INSERT INTO clstr_tst (b, c) VALUES (28, 'veintiocho');
INSERT INTO clstr_tst (b, c) VALUES (32, 'treinta y dos');
INSERT INTO clstr_tst (b, c) VALUES (5, 'cinco');
INSERT INTO clstr_tst (b, c) VALUES (29, 'veintinueve');
INSERT INTO clstr_tst (b, c) VALUES (1, 'uno');
INSERT INTO clstr_tst (b, c) VALUES (24, 'veinticuatro');
INSERT INTO clstr_tst (b, c) VALUES (30, 'treinta');
INSERT INTO clstr_tst (b, c) VALUES (12, 'doce');
INSERT INTO clstr_tst (b, c) VALUES (17, 'diecisiete');
INSERT INTO clstr_tst (b, c) VALUES (9, 'nueve');
INSERT INTO clstr_tst (b, c) VALUES (19, 'diecinueve');
INSERT INTO clstr_tst (b, c) VALUES (26, 'veintiseis');
INSERT INTO clstr_tst (b, c) VALUES (15, 'quince');
INSERT INTO clstr_tst (b, c) VALUES (7, 'siete');
INSERT INTO clstr_tst (b, c) VALUES (16, 'dieciseis');
INSERT INTO clstr_tst (b, c) VALUES (8, 'ocho');
2002-08-11 23:17:35 +02:00
-- This entry is needed to test that TOASTED values are copied correctly.
INSERT INTO clstr_tst (b, c, d) VALUES (6, 'seis', repeat('xyzzy', 100000));
2002-08-10 22:43:46 +02:00
CLUSTER clstr_tst_c ON clstr_tst;
2002-08-11 23:17:35 +02:00
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst;
a | b | c | substring | length
----+----+---------------+--------------------------------+--------
10 | 14 | catorce | |
18 | 5 | cinco | |
9 | 4 | cuatro | |
26 | 19 | diecinueve | |
12 | 18 | dieciocho | |
30 | 16 | dieciseis | |
24 | 17 | diecisiete | |
2 | 10 | diez | |
23 | 12 | doce | |
11 | 2 | dos | |
25 | 9 | nueve | |
31 | 8 | ocho | |
1 | 11 | once | |
28 | 15 | quince | |
32 | 6 | seis | xyzzyxyzzyxyzzyxyzzyxyzzyxyzzy | 500000
29 | 7 | siete | |
15 | 13 | trece | |
22 | 30 | treinta | |
17 | 32 | treinta y dos | |
3 | 31 | treinta y uno | |
5 | 3 | tres | |
20 | 1 | uno | |
6 | 20 | veinte | |
14 | 25 | veinticinco | |
21 | 24 | veinticuatro | |
4 | 22 | veintidos | |
19 | 29 | veintinueve | |
16 | 28 | veintiocho | |
27 | 26 | veintiseis | |
13 | 27 | veintisiete | |
7 | 23 | veintitres | |
8 | 21 | veintiuno | |
2002-08-10 22:43:46 +02:00
(32 rows)
2002-08-11 23:17:35 +02:00
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst ORDER BY a;
a | b | c | substring | length
----+----+---------------+--------------------------------+--------
1 | 11 | once | |
2 | 10 | diez | |
3 | 31 | treinta y uno | |
4 | 22 | veintidos | |
5 | 3 | tres | |
6 | 20 | veinte | |
7 | 23 | veintitres | |
8 | 21 | veintiuno | |
9 | 4 | cuatro | |
10 | 14 | catorce | |
11 | 2 | dos | |
12 | 18 | dieciocho | |
13 | 27 | veintisiete | |
14 | 25 | veinticinco | |
15 | 13 | trece | |
16 | 28 | veintiocho | |
17 | 32 | treinta y dos | |
18 | 5 | cinco | |
19 | 29 | veintinueve | |
20 | 1 | uno | |
21 | 24 | veinticuatro | |
22 | 30 | treinta | |
23 | 12 | doce | |
24 | 17 | diecisiete | |
25 | 9 | nueve | |
26 | 19 | diecinueve | |
27 | 26 | veintiseis | |
28 | 15 | quince | |
29 | 7 | siete | |
30 | 16 | dieciseis | |
31 | 8 | ocho | |
32 | 6 | seis | xyzzyxyzzyxyzzyxyzzyxyzzyxyzzy | 500000
2002-08-10 22:43:46 +02:00
(32 rows)
2002-08-11 23:17:35 +02:00
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst ORDER BY b;
a | b | c | substring | length
----+----+---------------+--------------------------------+--------
20 | 1 | uno | |
11 | 2 | dos | |
5 | 3 | tres | |
9 | 4 | cuatro | |
18 | 5 | cinco | |
32 | 6 | seis | xyzzyxyzzyxyzzyxyzzyxyzzyxyzzy | 500000
29 | 7 | siete | |
31 | 8 | ocho | |
25 | 9 | nueve | |
2 | 10 | diez | |
1 | 11 | once | |
23 | 12 | doce | |
15 | 13 | trece | |
10 | 14 | catorce | |
28 | 15 | quince | |
30 | 16 | dieciseis | |
24 | 17 | diecisiete | |
12 | 18 | dieciocho | |
26 | 19 | diecinueve | |
6 | 20 | veinte | |
8 | 21 | veintiuno | |
4 | 22 | veintidos | |
7 | 23 | veintitres | |
21 | 24 | veinticuatro | |
14 | 25 | veinticinco | |
27 | 26 | veintiseis | |
13 | 27 | veintisiete | |
16 | 28 | veintiocho | |
19 | 29 | veintinueve | |
22 | 30 | treinta | |
3 | 31 | treinta y uno | |
17 | 32 | treinta y dos | |
2002-08-10 22:43:46 +02:00
(32 rows)
2002-08-11 23:17:35 +02:00
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst ORDER BY c;
a | b | c | substring | length
----+----+---------------+--------------------------------+--------
10 | 14 | catorce | |
18 | 5 | cinco | |
9 | 4 | cuatro | |
26 | 19 | diecinueve | |
12 | 18 | dieciocho | |
30 | 16 | dieciseis | |
24 | 17 | diecisiete | |
2 | 10 | diez | |
23 | 12 | doce | |
11 | 2 | dos | |
25 | 9 | nueve | |
31 | 8 | ocho | |
1 | 11 | once | |
28 | 15 | quince | |
32 | 6 | seis | xyzzyxyzzyxyzzyxyzzyxyzzyxyzzy | 500000
29 | 7 | siete | |
15 | 13 | trece | |
22 | 30 | treinta | |
17 | 32 | treinta y dos | |
3 | 31 | treinta y uno | |
5 | 3 | tres | |
20 | 1 | uno | |
6 | 20 | veinte | |
14 | 25 | veinticinco | |
21 | 24 | veinticuatro | |
4 | 22 | veintidos | |
19 | 29 | veintinueve | |
16 | 28 | veintiocho | |
27 | 26 | veintiseis | |
13 | 27 | veintisiete | |
7 | 23 | veintitres | |
8 | 21 | veintiuno | |
2002-08-10 22:43:46 +02:00
(32 rows)
2002-08-11 04:06:32 +02:00
-- Verify that inheritance link still works
INSERT INTO clstr_tst_inh VALUES (0, 100, 'in child table');
2002-08-11 23:17:35 +02:00
SELECT a,b,c,substring(d for 30), length(d) from clstr_tst;
a | b | c | substring | length
----+-----+----------------+--------------------------------+--------
10 | 14 | catorce | |
18 | 5 | cinco | |
9 | 4 | cuatro | |
26 | 19 | diecinueve | |
12 | 18 | dieciocho | |
30 | 16 | dieciseis | |
24 | 17 | diecisiete | |
2 | 10 | diez | |
23 | 12 | doce | |
11 | 2 | dos | |
25 | 9 | nueve | |
31 | 8 | ocho | |
1 | 11 | once | |
28 | 15 | quince | |
32 | 6 | seis | xyzzyxyzzyxyzzyxyzzyxyzzyxyzzy | 500000
29 | 7 | siete | |
15 | 13 | trece | |
22 | 30 | treinta | |
17 | 32 | treinta y dos | |
3 | 31 | treinta y uno | |
5 | 3 | tres | |
20 | 1 | uno | |
6 | 20 | veinte | |
14 | 25 | veinticinco | |
21 | 24 | veinticuatro | |
4 | 22 | veintidos | |
19 | 29 | veintinueve | |
16 | 28 | veintiocho | |
27 | 26 | veintiseis | |
13 | 27 | veintisiete | |
7 | 23 | veintitres | |
8 | 21 | veintiuno | |
0 | 100 | in child table | |
2002-08-11 04:06:32 +02:00
(33 rows)
-- Verify that foreign key link still works
INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail');
2003-09-25 08:58:07 +02:00
ERROR: insert or update on table "clstr_tst" violates foreign key constraint "clstr_tst_con"
DETAIL: Key (b)=(1111) is not present in table "clstr_tst_s".
2007-09-03 03:28:40 +02:00
SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass
ORDER BY 1;
Catalog not-null constraints
We now create contype='n' pg_constraint rows for not-null constraints.
We propagate these constraints to other tables during operations such as
adding inheritance relationships, creating and attaching partitions and
creating tables LIKE other tables. We also spawn not-null constraints
for inheritance child tables when their parents have primary keys.
These related constraints mostly follow the well-known rules of
conislocal and coninhcount that we have for CHECK constraints, with some
adaptations: for example, as opposed to CHECK constraints, we don't
match not-null ones by name when descending a hierarchy to alter it,
instead matching by column name that they apply to. This means we don't
require the constraint names to be identical across a hierarchy.
For now, we omit them for system catalogs. Maybe this is worth
reconsidering. We don't support NOT VALID nor DEFERRABLE clauses
either; these can be added as separate features later (this patch is
already large and complicated enough.)
psql shows these constraints in \d+.
pg_dump requires some ad-hoc hacks, particularly when dumping a primary
key. We now create one "throwaway" not-null constraint for each column
in the PK together with the CREATE TABLE command, and once the PK is
created, all those throwaway constraints are removed. This avoids
having to check each tuple for nullness when the dump restores the
primary key creation.
pg_upgrading from an older release requires a somewhat brittle procedure
to create a constraint state that matches what would be created if the
database were being created fresh in Postgres 17. I have tested all the
scenarios I could think of, and it works correctly as far as I can tell,
but I could have neglected weird cases.
This patch has been very long in the making. The first patch was
written by Bernd Helmle in 2010 to add a new pg_constraint.contype value
('n'), which I (Álvaro) then hijacked in 2011 and 2012, until that one
was killed by the realization that we ought to use contype='c' instead:
manufactured CHECK constraints. However, later SQL standard
development, as well as nonobvious emergent properties of that design
(mostly, failure to distinguish them from "normal" CHECK constraints as
well as the performance implication of having to test the CHECK
expression) led us to reconsider this choice, so now the current
implementation uses contype='n' again. During Postgres 16 this had
already been introduced by commit e056c557aef4, but there were some
problems mainly with the pg_upgrade procedure that couldn't be fixed in
reasonable time, so it was reverted.
In 2016 Vitaly Burovoy also worked on this feature[1] but found no
consensus for his proposed approach, which was claimed to be closer to
the letter of the standard, requiring an additional pg_attribute column
to track the OID of the not-null constraint for that column.
[1] https://postgr.es/m/CAKOSWNkN6HSyatuys8xZxzRCR-KL1OkHS5-b9qd9bf1Rad3PLA@mail.gmail.com
Author: Álvaro Herrera <alvherre@alvh.no-ip.org>
Author: Bernd Helmle <mailings@oopsware.de>
Reviewed-by: Justin Pryzby <pryzby@telsasoft.com>
Reviewed-by: Peter Eisentraut <peter.eisentraut@enterprisedb.com>
Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>
2023-08-25 13:31:24 +02:00
conname
----------------------
clstr_tst_a_not_null
2002-08-10 22:43:46 +02:00
clstr_tst_con
2007-09-03 03:28:40 +02:00
clstr_tst_pkey
Catalog not-null constraints
We now create contype='n' pg_constraint rows for not-null constraints.
We propagate these constraints to other tables during operations such as
adding inheritance relationships, creating and attaching partitions and
creating tables LIKE other tables. We also spawn not-null constraints
for inheritance child tables when their parents have primary keys.
These related constraints mostly follow the well-known rules of
conislocal and coninhcount that we have for CHECK constraints, with some
adaptations: for example, as opposed to CHECK constraints, we don't
match not-null ones by name when descending a hierarchy to alter it,
instead matching by column name that they apply to. This means we don't
require the constraint names to be identical across a hierarchy.
For now, we omit them for system catalogs. Maybe this is worth
reconsidering. We don't support NOT VALID nor DEFERRABLE clauses
either; these can be added as separate features later (this patch is
already large and complicated enough.)
psql shows these constraints in \d+.
pg_dump requires some ad-hoc hacks, particularly when dumping a primary
key. We now create one "throwaway" not-null constraint for each column
in the PK together with the CREATE TABLE command, and once the PK is
created, all those throwaway constraints are removed. This avoids
having to check each tuple for nullness when the dump restores the
primary key creation.
pg_upgrading from an older release requires a somewhat brittle procedure
to create a constraint state that matches what would be created if the
database were being created fresh in Postgres 17. I have tested all the
scenarios I could think of, and it works correctly as far as I can tell,
but I could have neglected weird cases.
This patch has been very long in the making. The first patch was
written by Bernd Helmle in 2010 to add a new pg_constraint.contype value
('n'), which I (Álvaro) then hijacked in 2011 and 2012, until that one
was killed by the realization that we ought to use contype='c' instead:
manufactured CHECK constraints. However, later SQL standard
development, as well as nonobvious emergent properties of that design
(mostly, failure to distinguish them from "normal" CHECK constraints as
well as the performance implication of having to test the CHECK
expression) led us to reconsider this choice, so now the current
implementation uses contype='n' again. During Postgres 16 this had
already been introduced by commit e056c557aef4, but there were some
problems mainly with the pg_upgrade procedure that couldn't be fixed in
reasonable time, so it was reverted.
In 2016 Vitaly Burovoy also worked on this feature[1] but found no
consensus for his proposed approach, which was claimed to be closer to
the letter of the standard, requiring an additional pg_attribute column
to track the OID of the not-null constraint for that column.
[1] https://postgr.es/m/CAKOSWNkN6HSyatuys8xZxzRCR-KL1OkHS5-b9qd9bf1Rad3PLA@mail.gmail.com
Author: Álvaro Herrera <alvherre@alvh.no-ip.org>
Author: Bernd Helmle <mailings@oopsware.de>
Reviewed-by: Justin Pryzby <pryzby@telsasoft.com>
Reviewed-by: Peter Eisentraut <peter.eisentraut@enterprisedb.com>
Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>
2023-08-25 13:31:24 +02:00
(3 rows)
2002-08-10 22:43:46 +02:00
2002-08-11 23:17:35 +02:00
SELECT relname, relkind,
EXISTS(SELECT 1 FROM pg_class WHERE oid = c.reltoastrelid) AS hastoast
FROM pg_class c WHERE relname LIKE 'clstr_tst%' ORDER BY relname;
relname | relkind | hastoast
----------------------+---------+----------
clstr_tst | r | t
clstr_tst_a_seq | S | f
clstr_tst_b | i | f
clstr_tst_b_c | i | f
clstr_tst_c | i | f
clstr_tst_c_b | i | f
clstr_tst_inh | r | t
clstr_tst_pkey | i | f
clstr_tst_s | r | f
clstr_tst_s_pkey | i | f
clstr_tst_s_rf_a_seq | S | f
2002-08-10 22:43:46 +02:00
(11 rows)
2002-08-27 05:38:28 +02:00
-- Verify that indisclustered is correctly set
SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2
WHERE pg_class.oid=indexrelid
AND indrelid=pg_class_2.oid
AND pg_class_2.relname = 'clstr_tst'
AND indisclustered;
relname
-------------
clstr_tst_c
(1 row)
2003-03-20 19:52:48 +01:00
-- Try changing indisclustered
ALTER TABLE clstr_tst CLUSTER ON clstr_tst_b_c;
SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2
WHERE pg_class.oid=indexrelid
AND indrelid=pg_class_2.oid
AND pg_class_2.relname = 'clstr_tst'
AND indisclustered;
relname
---------------
clstr_tst_b_c
(1 row)
2004-06-02 23:01:10 +02:00
-- Try turning off all clustering
ALTER TABLE clstr_tst SET WITHOUT CLUSTER;
SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2
WHERE pg_class.oid=indexrelid
AND indrelid=pg_class_2.oid
AND pg_class_2.relname = 'clstr_tst'
2004-06-10 19:56:03 +02:00
AND indisclustered;
relname
---------
(0 rows)
2004-06-02 23:01:10 +02:00
2022-04-02 19:08:34 +02:00
-- Verify that toast tables are clusterable
CLUSTER pg_toast.pg_toast_826 USING pg_toast_826_index;
2002-11-15 04:09:39 +01:00
-- Verify that clustering all tables does in fact cluster the right ones
2016-07-18 00:42:31 +02:00
CREATE USER regress_clstr_user;
2002-11-15 04:09:39 +01:00
CREATE TABLE clstr_1 (a INT PRIMARY KEY);
CREATE TABLE clstr_2 (a INT PRIMARY KEY);
CREATE TABLE clstr_3 (a INT PRIMARY KEY);
2016-07-18 00:42:31 +02:00
ALTER TABLE clstr_1 OWNER TO regress_clstr_user;
ALTER TABLE clstr_3 OWNER TO regress_clstr_user;
GRANT SELECT ON clstr_2 TO regress_clstr_user;
2002-11-15 04:09:39 +01:00
INSERT INTO clstr_1 VALUES (2);
INSERT INTO clstr_1 VALUES (1);
INSERT INTO clstr_2 VALUES (2);
INSERT INTO clstr_2 VALUES (1);
INSERT INTO clstr_3 VALUES (2);
INSERT INTO clstr_3 VALUES (1);
2002-11-16 04:25:22 +01:00
-- "CLUSTER <tablename>" on a table that hasn't been clustered
CLUSTER clstr_2;
2003-07-20 23:56:35 +02:00
ERROR: there is no previously clustered index for table "clstr_2"
2002-11-15 04:09:39 +01:00
CLUSTER clstr_1_pkey ON clstr_1;
2007-04-08 02:26:34 +02:00
CLUSTER clstr_2 USING clstr_2_pkey;
2002-11-15 04:09:39 +01:00
SELECT * FROM clstr_1 UNION ALL
SELECT * FROM clstr_2 UNION ALL
SELECT * FROM clstr_3;
a
---
1
2
1
2
2
1
(6 rows)
-- revert to the original state
DELETE FROM clstr_1;
DELETE FROM clstr_2;
DELETE FROM clstr_3;
INSERT INTO clstr_1 VALUES (2);
INSERT INTO clstr_1 VALUES (1);
INSERT INTO clstr_2 VALUES (2);
INSERT INTO clstr_2 VALUES (1);
INSERT INTO clstr_3 VALUES (2);
INSERT INTO clstr_3 VALUES (1);
-- this user can only cluster clstr_1 and clstr_3, but the latter
-- has not been clustered
2016-07-18 00:42:31 +02:00
SET SESSION AUTHORIZATION regress_clstr_user;
2002-11-19 18:39:00 +01:00
CLUSTER;
2002-11-15 04:09:39 +01:00
SELECT * FROM clstr_1 UNION ALL
SELECT * FROM clstr_2 UNION ALL
SELECT * FROM clstr_3;
a
---
1
2
2
1
2
1
(6 rows)
2002-11-16 04:25:22 +01:00
-- cluster a single table using the indisclustered bit previously set
DELETE FROM clstr_1;
INSERT INTO clstr_1 VALUES (2);
INSERT INTO clstr_1 VALUES (1);
CLUSTER clstr_1;
SELECT * FROM clstr_1;
a
---
1
2
(2 rows)
2007-04-08 03:26:33 +02:00
-- Test MVCC-safety of cluster. There isn't much we can do to verify the
-- results with a single backend...
CREATE TABLE clustertest (key int PRIMARY KEY);
INSERT INTO clustertest VALUES (10);
INSERT INTO clustertest VALUES (20);
INSERT INTO clustertest VALUES (30);
INSERT INTO clustertest VALUES (40);
INSERT INTO clustertest VALUES (50);
-- Use a transaction so that updates are not committed when CLUSTER sees 'em
BEGIN;
-- Test update where the old row version is found first in the scan
UPDATE clustertest SET key = 100 WHERE key = 10;
-- Test update where the new row version is found first in the scan
UPDATE clustertest SET key = 35 WHERE key = 40;
2010-11-23 21:27:50 +01:00
-- Test longer update chain
2007-04-08 03:26:33 +02:00
UPDATE clustertest SET key = 60 WHERE key = 50;
UPDATE clustertest SET key = 70 WHERE key = 60;
UPDATE clustertest SET key = 80 WHERE key = 70;
SELECT * FROM clustertest;
key
-----
20
30
100
35
80
(5 rows)
CLUSTER clustertest_pkey ON clustertest;
SELECT * FROM clustertest;
key
-----
20
30
35
80
100
(5 rows)
COMMIT;
SELECT * FROM clustertest;
key
-----
20
30
35
80
100
(5 rows)
2010-02-02 20:12:29 +01:00
-- check that temp tables can be clustered
create temp table clstr_temp (col1 int primary key, col2 text);
insert into clstr_temp values (2, 'two'), (1, 'one');
cluster clstr_temp using clstr_temp_pkey;
select * from clstr_temp;
col1 | col2
------+------
1 | one
2 | two
(2 rows)
2010-02-03 04:21:25 +01:00
drop table clstr_temp;
2016-07-13 21:23:56 +02:00
RESET SESSION AUTHORIZATION;
2021-03-23 16:24:16 +01:00
-- check clustering an empty table
DROP TABLE clustertest;
CREATE TABLE clustertest (f1 int PRIMARY KEY);
CLUSTER clustertest USING clustertest_pkey;
CLUSTER clustertest;
2022-04-02 19:08:34 +02:00
-- Check that partitioned tables can be clustered
2018-01-25 20:11:51 +01:00
CREATE TABLE clstrpart (a int) PARTITION BY RANGE (a);
2022-04-02 19:08:34 +02:00
CREATE TABLE clstrpart1 PARTITION OF clstrpart FOR VALUES FROM (1) TO (10) PARTITION BY RANGE (a);
CREATE TABLE clstrpart11 PARTITION OF clstrpart1 FOR VALUES FROM (1) TO (5);
CREATE TABLE clstrpart12 PARTITION OF clstrpart1 FOR VALUES FROM (5) TO (10) PARTITION BY RANGE (a);
CREATE TABLE clstrpart2 PARTITION OF clstrpart FOR VALUES FROM (10) TO (20);
CREATE TABLE clstrpart3 PARTITION OF clstrpart DEFAULT PARTITION BY RANGE (a);
CREATE TABLE clstrpart33 PARTITION OF clstrpart3 DEFAULT;
CREATE INDEX clstrpart_only_idx ON ONLY clstrpart (a);
CLUSTER clstrpart USING clstrpart_only_idx; -- fails
ERROR: cannot cluster on invalid index "clstrpart_only_idx"
DROP INDEX clstrpart_only_idx;
2018-01-25 20:11:51 +01:00
CREATE INDEX clstrpart_idx ON clstrpart (a);
2022-04-02 19:08:34 +02:00
-- Check that clustering sets new relfilenodes:
CREATE TEMP TABLE old_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ;
CLUSTER clstrpart USING clstrpart_idx;
CREATE TEMP TABLE new_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ;
SELECT relname, old.level, old.relkind, old.relfilenode = new.relfilenode FROM old_cluster_info AS old JOIN new_cluster_info AS new USING (relname) ORDER BY relname COLLATE "C";
relname | level | relkind | ?column?
-------------+-------+---------+----------
clstrpart | 0 | p | t
clstrpart1 | 1 | p | t
clstrpart11 | 2 | r | f
clstrpart12 | 2 | p | t
clstrpart2 | 1 | r | f
clstrpart3 | 1 | p | t
clstrpart33 | 2 | r | f
(7 rows)
-- Partitioned indexes aren't and can't be marked un/clustered:
\d clstrpart
Partitioned table "public.clstrpart"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
a | integer | | |
Partition key: RANGE (a)
Indexes:
"clstrpart_idx" btree (a)
Number of partitions: 3 (Use \d+ to list them.)
CLUSTER clstrpart;
ERROR: there is no previously clustered index for table "clstrpart"
ALTER TABLE clstrpart SET WITHOUT CLUSTER;
ERROR: cannot mark index clustered in partitioned table
2018-01-25 20:11:51 +01:00
ALTER TABLE clstrpart CLUSTER ON clstrpart_idx;
ERROR: cannot mark index clustered in partitioned table
DROP TABLE clstrpart;
2022-04-14 22:11:06 +02:00
-- Ownership of partitions is checked
CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
CREATE ROLE regress_ptnowner;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
relname | ?column?
-----------+----------
ptnowner | t
ptnowner1 | f
Fix cache lookup hazards introduced by ff9618e82a.
ff9618e82a introduced has_partition_ancestor_privs(), which is used
to check whether a user has MAINTAIN on any partition ancestors.
This involves syscache lookups, and presently this function does
not take any relation locks, so it is likely subject to the same
kind of cache lookup failures that were fixed by 19de0ab23c.
To fix this problem, this commit partially reverts ff9618e82a.
Specifically, it removes the partition-related changes, including
the has_partition_ancestor_privs() function mentioned above. This
means that MAINTAIN on a partitioned table is no longer sufficient
to perform maintenance commands on its partitions. This is more
like how privileges for maintenance commands work on supported
versions. Privileges are checked for each partition, so a command
that flows down to all partitions might refuse to process them
(e.g., if the current user doesn't have MAINTAIN on the partition).
In passing, adjust a few related comments and error messages, and
add a test for the privilege checks for CLUSTER on a partitioned
table.
Reviewed-by: Michael Paquier, Jeff Davis
Discussion: https://postgr.es/m/20230613211246.GA219055%40nathanxps13
2023-06-23 00:48:20 +02:00
ptnowner2 | t
2022-04-14 22:11:06 +02:00
(3 rows)
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
2016-07-13 21:23:56 +02:00
-- Test CLUSTER with external tuplesorting
create table clstr_4 as select * from tenk1;
create index cluster_sort on clstr_4 (hundred, thousand, tenthous);
-- ensure we don't use the index in CLUSTER nor the checking SELECTs
set enable_indexscan = off;
2017-09-29 16:20:44 +02:00
-- Use external sort:
2016-07-13 21:23:56 +02:00
set maintenance_work_mem = '1MB';
cluster clstr_4 using cluster_sort;
select * from
(select hundred, lag(hundred) over () as lhundred,
thousand, lag(thousand) over () as lthousand,
tenthous, lag(tenthous) over () as ltenthous from clstr_4) ss
where row(hundred, thousand, tenthous) <= row(lhundred, lthousand, ltenthous);
hundred | lhundred | thousand | lthousand | tenthous | ltenthous
---------+----------+----------+-----------+----------+-----------
(0 rows)
reset enable_indexscan;
reset maintenance_work_mem;
2019-10-15 19:40:13 +02:00
-- test CLUSTER on expression index
CREATE TABLE clstr_expression(id serial primary key, a int, b text COLLATE "C");
INSERT INTO clstr_expression(a, b) SELECT g.i % 42, 'prefix'||g.i FROM generate_series(1, 133) g(i);
CREATE INDEX clstr_expression_minus_a ON clstr_expression ((-a), b);
CREATE INDEX clstr_expression_upper_b ON clstr_expression ((upper(b)));
-- verify indexes work before cluster
BEGIN;
SET LOCAL enable_seqscan = false;
EXPLAIN (COSTS OFF) SELECT * FROM clstr_expression WHERE upper(b) = 'PREFIX3';
QUERY PLAN
---------------------------------------------------------------
Index Scan using clstr_expression_upper_b on clstr_expression
Index Cond: (upper(b) = 'PREFIX3'::text)
(2 rows)
SELECT * FROM clstr_expression WHERE upper(b) = 'PREFIX3';
id | a | b
----+---+---------
3 | 3 | prefix3
(1 row)
EXPLAIN (COSTS OFF) SELECT * FROM clstr_expression WHERE -a = -3 ORDER BY -a, b;
QUERY PLAN
---------------------------------------------------------------
Index Scan using clstr_expression_minus_a on clstr_expression
Index Cond: ((- a) = '-3'::integer)
(2 rows)
SELECT * FROM clstr_expression WHERE -a = -3 ORDER BY -a, b;
id | a | b
-----+---+-----------
129 | 3 | prefix129
3 | 3 | prefix3
45 | 3 | prefix45
87 | 3 | prefix87
(4 rows)
COMMIT;
-- and after clustering on clstr_expression_minus_a
CLUSTER clstr_expression USING clstr_expression_minus_a;
2022-04-21 02:17:43 +02:00
WITH rows AS
(SELECT ctid, lag(a) OVER (ORDER BY ctid) AS la, a FROM clstr_expression)
SELECT * FROM rows WHERE la < a;
ctid | la | a
------+----+---
(0 rows)
2019-10-15 19:40:13 +02:00
BEGIN;
SET LOCAL enable_seqscan = false;
EXPLAIN (COSTS OFF) SELECT * FROM clstr_expression WHERE upper(b) = 'PREFIX3';
QUERY PLAN
---------------------------------------------------------------
Index Scan using clstr_expression_upper_b on clstr_expression
Index Cond: (upper(b) = 'PREFIX3'::text)
(2 rows)
SELECT * FROM clstr_expression WHERE upper(b) = 'PREFIX3';
id | a | b
----+---+---------
3 | 3 | prefix3
(1 row)
EXPLAIN (COSTS OFF) SELECT * FROM clstr_expression WHERE -a = -3 ORDER BY -a, b;
QUERY PLAN
---------------------------------------------------------------
Index Scan using clstr_expression_minus_a on clstr_expression
Index Cond: ((- a) = '-3'::integer)
(2 rows)
SELECT * FROM clstr_expression WHERE -a = -3 ORDER BY -a, b;
id | a | b
-----+---+-----------
129 | 3 | prefix129
3 | 3 | prefix3
45 | 3 | prefix45
87 | 3 | prefix87
(4 rows)
COMMIT;
-- and after clustering on clstr_expression_upper_b
CLUSTER clstr_expression USING clstr_expression_upper_b;
2022-04-21 02:17:43 +02:00
WITH rows AS
(SELECT ctid, lag(b) OVER (ORDER BY ctid) AS lb, b FROM clstr_expression)
SELECT * FROM rows WHERE upper(lb) > upper(b);
ctid | lb | b
------+----+---
(0 rows)
2019-10-15 19:40:13 +02:00
BEGIN;
SET LOCAL enable_seqscan = false;
EXPLAIN (COSTS OFF) SELECT * FROM clstr_expression WHERE upper(b) = 'PREFIX3';
QUERY PLAN
---------------------------------------------------------------
Index Scan using clstr_expression_upper_b on clstr_expression
Index Cond: (upper(b) = 'PREFIX3'::text)
(2 rows)
SELECT * FROM clstr_expression WHERE upper(b) = 'PREFIX3';
id | a | b
----+---+---------
3 | 3 | prefix3
(1 row)
EXPLAIN (COSTS OFF) SELECT * FROM clstr_expression WHERE -a = -3 ORDER BY -a, b;
QUERY PLAN
---------------------------------------------------------------
Index Scan using clstr_expression_minus_a on clstr_expression
Index Cond: ((- a) = '-3'::integer)
(2 rows)
SELECT * FROM clstr_expression WHERE -a = -3 ORDER BY -a, b;
id | a | b
-----+---+-----------
129 | 3 | prefix129
3 | 3 | prefix3
45 | 3 | prefix45
87 | 3 | prefix87
(4 rows)
COMMIT;
2002-11-15 04:09:39 +01:00
-- clean up
2007-04-08 03:26:33 +02:00
DROP TABLE clustertest;
2002-11-15 04:09:39 +01:00
DROP TABLE clstr_1;
2005-07-07 22:40:02 +02:00
DROP TABLE clstr_2;
2002-11-15 04:09:39 +01:00
DROP TABLE clstr_3;
2016-07-13 21:23:56 +02:00
DROP TABLE clstr_4;
2019-10-15 19:40:13 +02:00
DROP TABLE clstr_expression;
2016-07-18 00:42:31 +02:00
DROP USER regress_clstr_user;